synth-ai 0.22.2.dev688__tar.gz → 0.22.2.dev689__tar.gz

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 (360) hide show
  1. {synth_ai-0.22.2.dev688/synth_ai.egg-info → synth_ai-0.22.2.dev689}/PKG-INFO +1 -1
  2. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/pyproject.toml +1 -1
  3. synth_ai-0.22.2.dev689/synth_ai/core/http/retry.py +302 -0
  4. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/http/transport.py +81 -12
  5. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/client.py +31 -9
  6. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689/synth_ai.egg-info}/PKG-INFO +1 -1
  7. synth_ai-0.22.2.dev688/synth_ai/core/http/retry.py +0 -135
  8. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/LICENSE +0 -0
  9. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/MANIFEST.in +0 -0
  10. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/README.md +0 -0
  11. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/openapi/index-v1.json +0 -0
  12. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/openapi/messaging-v1.json +0 -0
  13. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/openapi/research-v1.json +0 -0
  14. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/setup.cfg +0 -0
  15. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/setup.py +0 -0
  16. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/README.md +0 -0
  17. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/__init__.py +0 -0
  18. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/__main__.py +0 -0
  19. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/cli/AGENTS.md +0 -0
  20. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/cli/README.md +0 -0
  21. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/cli/__init__.py +0 -0
  22. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/cli/__main__.py +0 -0
  23. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/cli/index.py +0 -0
  24. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/cli/main.py +0 -0
  25. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/cli/research.py +0 -0
  26. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/cli/research_environments.py +0 -0
  27. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/cli/research_image_releases.py +0 -0
  28. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/cli/research_projects.py +0 -0
  29. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/cli/utils/__init__.py +0 -0
  30. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/cli/utils/research_launch.py +0 -0
  31. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/client.py +0 -0
  32. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/README.md +0 -0
  33. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/__init__.py +0 -0
  34. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/auth/__init__.py +0 -0
  35. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/auth/credentials.py +0 -0
  36. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/contracts/__init__.py +0 -0
  37. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/contracts/json_value.py +0 -0
  38. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/contracts/pagination.py +0 -0
  39. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/errors.py +0 -0
  40. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/http/__init__.py +0 -0
  41. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/http/async_transport.py +0 -0
  42. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/http/request.py +0 -0
  43. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/http/streaming.py +0 -0
  44. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/local_files.py +0 -0
  45. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/research/__init__.py +0 -0
  46. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/utils/__init__.py +0 -0
  47. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/utils/env.py +0 -0
  48. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/utils/log_filter.py +0 -0
  49. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/utils/paths.py +0 -0
  50. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/core/utils/urls.py +0 -0
  51. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/README.md +0 -0
  52. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/__init__.py +0 -0
  53. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/__main__.py +0 -0
  54. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/objective_tools.py +0 -0
  55. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/registry.py +0 -0
  56. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/request_models.py +0 -0
  57. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/server.py +0 -0
  58. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/__init__.py +0 -0
  59. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/approvals.py +0 -0
  60. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/artifacts.py +0 -0
  61. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/datasets.py +0 -0
  62. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/environments.py +0 -0
  63. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/exports.py +0 -0
  64. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/factories.py +0 -0
  65. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/factory_results.py +0 -0
  66. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/files.py +0 -0
  67. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/image_releases.py +0 -0
  68. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/index.py +0 -0
  69. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/index_qa.py +0 -0
  70. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/integrations.py +0 -0
  71. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/intern_program.py +0 -0
  72. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/launch_schemas.py +0 -0
  73. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/local_files.py +0 -0
  74. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/logs.py +0 -0
  75. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/models.py +0 -0
  76. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/outputs.py +0 -0
  77. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/policy_schemas.py +0 -0
  78. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/progress.py +0 -0
  79. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/project_data.py +0 -0
  80. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/projects.py +0 -0
  81. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/prs.py +0 -0
  82. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/readiness.py +0 -0
  83. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/repos.py +0 -0
  84. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/research_intern.py +0 -0
  85. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/resource_reads.py +0 -0
  86. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/resources.py +0 -0
  87. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/runs.py +0 -0
  88. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/scientific_records.py +0 -0
  89. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/trained_models.py +0 -0
  90. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/usage.py +0 -0
  91. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/visuals.py +0 -0
  92. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/workspace_inputs.py +0 -0
  93. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/pools.py +0 -0
  94. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/py.typed +0 -0
  95. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/README.md +0 -0
  96. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/__init__.py +0 -0
  97. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/AGENT_SETUP_PROMPT.md +0 -0
  98. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/README.md +0 -0
  99. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/__init__.py +0 -0
  100. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/agent_policy.py +0 -0
  101. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/artifacts.py +0 -0
  102. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/catalog.py +0 -0
  103. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/catalogue.py +0 -0
  104. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/classification.py +0 -0
  105. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/client.py +0 -0
  106. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/contracts.py +0 -0
  107. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/contributions.py +0 -0
  108. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/errors.py +0 -0
  109. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/forge_notices.py +0 -0
  110. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/intake.py +0 -0
  111. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/lifecycle.py +0 -0
  112. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/local_files.py +0 -0
  113. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/manifest.py +0 -0
  114. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/oauth.py +0 -0
  115. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/package.py +0 -0
  116. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/public.py +0 -0
  117. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/public_corpus.py +0 -0
  118. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/public_search.py +0 -0
  119. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/qa.py +0 -0
  120. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/qa_checks.py +0 -0
  121. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/qa_preflight.py +0 -0
  122. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/qa_reviews.py +0 -0
  123. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/research/__init__.py +0 -0
  124. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/research/build.py +0 -0
  125. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/research/capture.py +0 -0
  126. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/research/contracts.py +0 -0
  127. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/research/files.py +0 -0
  128. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/research/release.py +0 -0
  129. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/research_intake.py +0 -0
  130. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/retry.py +0 -0
  131. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/rights.py +0 -0
  132. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/scientific_provenance.py +0 -0
  133. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/scope_errors.py +0 -0
  134. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/scopes.py +0 -0
  135. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/search.py +0 -0
  136. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/submission.py +0 -0
  137. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/timeouts.py +0 -0
  138. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/transfer.py +0 -0
  139. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/upload_session.py +0 -0
  140. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/usage_accounting.py +0 -0
  141. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/value.py +0 -0
  142. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/wallet_search.py +0 -0
  143. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/messaging/README.md +0 -0
  144. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/messaging/__init__.py +0 -0
  145. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/messaging/client.py +0 -0
  146. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/messaging/contracts.py +0 -0
  147. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/optimizers/__init__.py +0 -0
  148. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/optimizers/client.py +0 -0
  149. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/optimizers/contracts.py +0 -0
  150. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/pagination.py +0 -0
  151. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/__init__.py +0 -0
  152. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/_internal/__init__.py +0 -0
  153. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/_internal/crypto.py +0 -0
  154. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/_internal/env.py +0 -0
  155. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/_internal/urls.py +0 -0
  156. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/account.py +0 -0
  157. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/advanced.py +0 -0
  158. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/advanced_factories.py +0 -0
  159. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/advanced_swarms.py +0 -0
  160. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/artifacts.py +0 -0
  161. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/auth.py +0 -0
  162. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/client.py +0 -0
  163. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/container_pools/__init__.py +0 -0
  164. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/container_pools/api.py +0 -0
  165. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/container_pools/contracts.py +0 -0
  166. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/container_pools/operations.py +0 -0
  167. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/container_pools/packaging.py +0 -0
  168. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/__init__.py +0 -0
  169. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/_activity_wire.py +0 -0
  170. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/_environment_wire.py +0 -0
  171. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/_transcript_wire.py +0 -0
  172. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/_wire.py +0 -0
  173. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/activity.py +0 -0
  174. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/activity_items.py +0 -0
  175. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/actor_images.py +0 -0
  176. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/billing.py +0 -0
  177. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/canonical_usage.py +0 -0
  178. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/checkpoints.py +0 -0
  179. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/cloud_deployment_claims.py +0 -0
  180. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/cloud_deployments.py +0 -0
  181. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/common.py +0 -0
  182. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/dataset_revisions.py +0 -0
  183. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/deliveries.py +0 -0
  184. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/dev_environment_evidence.py +0 -0
  185. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/economics.py +0 -0
  186. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/environment_manifest.py +0 -0
  187. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/environments.py +0 -0
  188. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/evidence.py +0 -0
  189. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/experiment_evidence.py +0 -0
  190. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/factories.py +0 -0
  191. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/factory_evidence.py +0 -0
  192. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/factory_lenses.py +0 -0
  193. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/factory_operations.py +0 -0
  194. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/factory_role_receipts.py +0 -0
  195. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/failure.py +0 -0
  196. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/forge/__init__.py +0 -0
  197. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/forge/contracts.py +0 -0
  198. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/forge/evaluation.py +0 -0
  199. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/forge/exports.py +0 -0
  200. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/forge/operations.py +0 -0
  201. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/forge/records.py +0 -0
  202. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/forge/resources.py +0 -0
  203. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/forge/schema.py +0 -0
  204. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/forge_citations.py +0 -0
  205. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/forge_uploads.py +0 -0
  206. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/image_releases.py +0 -0
  207. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/intern_authority.py +0 -0
  208. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/intern_blockers.py +0 -0
  209. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/intern_grants.py +0 -0
  210. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/intern_program.py +0 -0
  211. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/intern_resources.py +0 -0
  212. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/intern_usage.py +0 -0
  213. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/limit_evidence.py +0 -0
  214. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/local_execution_profile.py +0 -0
  215. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/native_attachment.py +0 -0
  216. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/native_result_outcome.py +0 -0
  217. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/operator_evidence.py +0 -0
  218. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/project.py +0 -0
  219. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/project_data.py +0 -0
  220. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/project_runtime.py +0 -0
  221. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/project_workspace.py +0 -0
  222. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/project_workspace_evidence.py +0 -0
  223. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/projects.py +0 -0
  224. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/promotions.py +0 -0
  225. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/research_intern.py +0 -0
  226. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/resource_reads.py +0 -0
  227. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/resource_settlement.py +0 -0
  228. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/run_authority.py +0 -0
  229. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/run_control.py +0 -0
  230. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/run_diagnostics.py +0 -0
  231. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/run_events.py +0 -0
  232. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/run_execution.py +0 -0
  233. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/run_launch.py +0 -0
  234. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/run_observability.py +0 -0
  235. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/run_state.py +0 -0
  236. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/run_timeline.py +0 -0
  237. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/runtime_image.py +0 -0
  238. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/runtime_intent.py +0 -0
  239. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/runtime_stream.py +0 -0
  240. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/runtime_stream_snapshot.py +0 -0
  241. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/scientific_citations.py +0 -0
  242. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/scientific_integrity.py +0 -0
  243. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_actor_models.py +0 -0
  244. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_actor_policy_data.py +0 -0
  245. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_agent_harnesses.py +0 -0
  246. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_agent_kinds.py +0 -0
  247. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_agent_models.py +0 -0
  248. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_branching.py +0 -0
  249. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_credential_providers.py +0 -0
  250. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_environment_kinds.py +0 -0
  251. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_evidence_obligations.py +0 -0
  252. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_funding_sources.py +0 -0
  253. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_horizons.py +0 -0
  254. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_host_kinds.py +0 -0
  255. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_inference_providers.py +0 -0
  256. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_network_topology.py +0 -0
  257. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_providers.py +0 -0
  258. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_resource_kinds.py +0 -0
  259. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_roles.py +0 -0
  260. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_run_policy.py +0 -0
  261. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_runbooks.py +0 -0
  262. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_runnable_project.py +0 -0
  263. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_runtime_kinds.py +0 -0
  264. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_tool_providers.py +0 -0
  265. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_work_modes.py +0 -0
  266. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/status.py +0 -0
  267. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/swarm_controls.py +0 -0
  268. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/swarm_history.py +0 -0
  269. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/swarm_rollouts.py +0 -0
  270. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/swarm_tasks.py +0 -0
  271. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/swarms.py +0 -0
  272. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/tag.py +0 -0
  273. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/traces.py +0 -0
  274. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/transcript.py +0 -0
  275. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/types.py +0 -0
  276. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/usage.py +0 -0
  277. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/visuals.py +0 -0
  278. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/wire_models.py +0 -0
  279. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/wire_models_README.md +0 -0
  280. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/work_products.py +0 -0
  281. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/workspaces.py +0 -0
  282. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/economics.py +0 -0
  283. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/efforts.py +0 -0
  284. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/environments.py +0 -0
  285. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/errors.py +0 -0
  286. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/events.py +0 -0
  287. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/experiments.py +0 -0
  288. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/facade.py +0 -0
  289. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/factories.py +0 -0
  290. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/factory_storage.py +0 -0
  291. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/image_releases.py +0 -0
  292. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/intern_authority.py +0 -0
  293. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/intern_blockers.py +0 -0
  294. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/intern_program.py +0 -0
  295. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/knowledge.py +0 -0
  296. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/models.py +0 -0
  297. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/operations.py +0 -0
  298. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/project_data.py +0 -0
  299. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/project_deliveries.py +0 -0
  300. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/project_workspaces.py +0 -0
  301. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/projects.py +0 -0
  302. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/public.py +0 -0
  303. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/readouts.py +0 -0
  304. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/research_intern.py +0 -0
  305. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/resource_reads.py +0 -0
  306. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/scientific_records.py +0 -0
  307. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/secrets.py +0 -0
  308. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/README.md +0 -0
  309. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/__init__.py +0 -0
  310. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/_base.py +0 -0
  311. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/_client_helpers.py +0 -0
  312. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/_run_authority_mixin.py +0 -0
  313. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/approvals.py +0 -0
  314. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/billing.py +0 -0
  315. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/cloud_deployments.py +0 -0
  316. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/config.py +0 -0
  317. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/cost.py +0 -0
  318. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/datasets.py +0 -0
  319. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/dev_environments.py +0 -0
  320. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/directory_inputs.py +0 -0
  321. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/environments.py +0 -0
  322. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/exports.py +0 -0
  323. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/factories.py +0 -0
  324. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/factory_evidence.py +0 -0
  325. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/files.py +0 -0
  326. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/github.py +0 -0
  327. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/image_releases.py +0 -0
  328. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/images.py +0 -0
  329. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/logs.py +0 -0
  330. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/models.py +0 -0
  331. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/outputs.py +0 -0
  332. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/progress.py +0 -0
  333. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/project.py +0 -0
  334. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/projects.py +0 -0
  335. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/prs.py +0 -0
  336. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/readiness.py +0 -0
  337. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/repositories.py +0 -0
  338. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/runs.py +0 -0
  339. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/secrets.py +0 -0
  340. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/setup.py +0 -0
  341. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/tag.py +0 -0
  342. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/trained_models.py +0 -0
  343. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/transport.py +0 -0
  344. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/usage.py +0 -0
  345. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/work_products.py +0 -0
  346. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/workspace_inputs.py +0 -0
  347. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/surface_parity.py +0 -0
  348. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/swarms.py +0 -0
  349. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/traces.py +0 -0
  350. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/transport/__init__.py +0 -0
  351. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/transport/http.py +0 -0
  352. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/transport/pagination.py +0 -0
  353. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/version.py +0 -0
  354. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/visuals.py +0 -0
  355. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/wiki.py +0 -0
  356. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai.egg-info/SOURCES.txt +0 -0
  357. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai.egg-info/dependency_links.txt +0 -0
  358. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai.egg-info/entry_points.txt +0 -0
  359. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai.egg-info/requires.txt +0 -0
  360. {synth_ai-0.22.2.dev688 → synth_ai-0.22.2.dev689}/synth_ai.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: synth-ai
3
- Version: 0.22.2.dev688
3
+ Version: 0.22.2.dev689
4
4
  Summary: Python SDK, CLI and MCP servers for Synth Index search and Swarms
5
5
  Author-email: Synth AI <josh@usesynth.ai>
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "synth-ai"
3
- version = "0.22.2.dev688"
3
+ version = "0.22.2.dev689"
4
4
  description = "Python SDK, CLI and MCP servers for Synth Index search and Swarms"
5
5
  keywords = ["synth", "index", "search", "citations", "research", "swarms", "mcp", "sdk"]
6
6
  authors = [{name = "Synth AI", email = "josh@usesynth.ai"}]
@@ -0,0 +1,302 @@
1
+ """Classified retry policy shared by sync and async transports.
2
+
3
+ # See: testing/specifications/sdk/core_research_migration.md
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import math
9
+ import random
10
+ from collections.abc import Mapping
11
+ from dataclasses import dataclass
12
+
13
+ from synth_ai.core.errors import (
14
+ ContractMismatchError,
15
+ RetryDirective,
16
+ RuntimeUnavailableError,
17
+ SynthError,
18
+ SynthErrorCategory,
19
+ SynthErrorCode,
20
+ SynthFailure,
21
+ )
22
+ from synth_ai.core.http.request import HttpMethod, HttpRequest, OperationMetadata
23
+
24
+
25
+ @dataclass(frozen=True, slots=True)
26
+ class RetryPolicy:
27
+ attempts_max: int = 3
28
+ delay_seconds_initial: float = 0.5
29
+ delay_seconds_max: float = 5.0
30
+ retry_statuses: frozenset[int] = frozenset({408, 409, 429, 500, 502, 503, 504})
31
+
32
+ def __post_init__(self) -> None:
33
+ if (
34
+ isinstance(self.attempts_max, bool)
35
+ or not isinstance(self.attempts_max, int)
36
+ or not 1 <= self.attempts_max <= 100
37
+ ):
38
+ raise ValueError("attempts_max must be an integer from 1 through 100")
39
+ for delay in (self.delay_seconds_initial, self.delay_seconds_max):
40
+ if (
41
+ isinstance(delay, bool)
42
+ or not isinstance(delay, (int, float))
43
+ or not math.isfinite(delay)
44
+ ):
45
+ raise ValueError("retry delays must be finite numbers")
46
+ if self.delay_seconds_initial < 0 or self.delay_seconds_max < 0:
47
+ raise ValueError("retry delays must be non-negative")
48
+ if self.delay_seconds_initial > self.delay_seconds_max:
49
+ raise ValueError("delay_seconds_initial cannot exceed delay_seconds_max")
50
+
51
+ def permits(
52
+ self,
53
+ operation: OperationMetadata,
54
+ *,
55
+ idempotency_key: str | None,
56
+ ) -> bool:
57
+ """Retry only metadata-declared idempotent operations.
58
+
59
+ Unsafe methods additionally require an idempotency key.
60
+ """
61
+ if not operation.idempotent:
62
+ return False
63
+ if operation.method is HttpMethod.GET:
64
+ return True
65
+ return bool(idempotency_key)
66
+
67
+ def delay_seconds(
68
+ self,
69
+ attempt_index: int,
70
+ *,
71
+ retry_after_seconds: float | None,
72
+ ) -> float:
73
+ """Deterministic exponential backoff capped by policy and Retry-After."""
74
+ if attempt_index < 0:
75
+ raise ValueError("attempt_index must be non-negative")
76
+ exponential = min(
77
+ self.delay_seconds_max,
78
+ self.delay_seconds_initial * (2**attempt_index),
79
+ )
80
+ if retry_after_seconds is None:
81
+ return exponential
82
+ if not math.isfinite(retry_after_seconds) or retry_after_seconds < 0:
83
+ raise ValueError("retry_after_seconds must be non-negative")
84
+ return min(self.delay_seconds_max, max(exponential, retry_after_seconds))
85
+
86
+
87
+ def idempotency_key_from_request(request: HttpRequest) -> str | None:
88
+ """Extract an idempotency key from headers or JSON body fields."""
89
+ values = [value for name, value in request.headers.items() if name.lower() == "idempotency-key"]
90
+ if isinstance(request.body, dict):
91
+ values.extend(
92
+ request.body[name]
93
+ for name in ("idempotency_key", "idempotency_key_run_create")
94
+ if name in request.body
95
+ )
96
+ identities = set()
97
+ for value in values:
98
+ if not isinstance(value, str) or not value.strip():
99
+ raise ValueError("idempotency identity must be a non-empty string")
100
+ identities.add(value)
101
+ if len(identities) > 1:
102
+ raise ValueError("request contains conflicting idempotency identities")
103
+ return next(iter(identities), None)
104
+
105
+
106
+ def should_retry_failure(
107
+ policy: RetryPolicy,
108
+ request: HttpRequest,
109
+ error: BaseException,
110
+ ) -> bool:
111
+ """Decide whether one failed execute attempt may be retried."""
112
+ if isinstance(error, (ContractMismatchError, RuntimeUnavailableError)):
113
+ return False
114
+ if not policy.permits(
115
+ request.operation,
116
+ idempotency_key=idempotency_key_from_request(request),
117
+ ):
118
+ return False
119
+ if isinstance(error, SynthError):
120
+ if error.failure is not None:
121
+ # The first-class directive is authoritative, including an explicit false.
122
+ return error.failure.retry.retryable
123
+ if error.retryable:
124
+ return True
125
+ status = None
126
+ if error.failure is not None:
127
+ status = error.failure.status
128
+ if status is None and hasattr(error, "status"):
129
+ status = error.status
130
+ return isinstance(status, int) and status in policy.retry_statuses
131
+ return False
132
+
133
+
134
+ def retry_after_from_error(error: BaseException) -> float | None:
135
+ if isinstance(error, SynthError):
136
+ return error.retry_after_seconds
137
+ return None
138
+
139
+
140
+ # Backend ``core/data/db/backpressure_classification.py::DB_BACKPRESSURE_REASON_CODE``,
141
+ # emitted as ``error_code`` by ``core/data/db/admission.py::db_backpressure_payload``.
142
+ DB_BACKPRESSURE_ERROR_CODE = "db_backpressure"
143
+ DB_BACKPRESSURE_RETRY_EXHAUSTED_CODE = "db_backpressure_retry_exhausted"
144
+ _REPLAY_SAFE_METHODS = frozenset({"GET", "HEAD", "OPTIONS"})
145
+
146
+
147
+ @dataclass(frozen=True, slots=True)
148
+ class DbBackpressureRefusal:
149
+ """One typed DB backpressure refusal decoded from a backend error body."""
150
+
151
+ status: int
152
+ error_code: str
153
+ component: str | None
154
+ work_class: str | None
155
+ retry_after_seconds: float | None
156
+ operator_action: str | None
157
+ message: str
158
+
159
+
160
+ @dataclass(frozen=True, slots=True)
161
+ class DbBackpressureRetryPolicy:
162
+ """Bounds for replaying a request the backend shed before starting DB work.
163
+
164
+ Attempts and total sleep are both bounded; the server's ``retry_after`` is
165
+ honored up to ``retry_after_cap_seconds`` and only ever lengthened by jitter.
166
+ """
167
+
168
+ attempts_max: int = 5
169
+ total_wait_seconds_max: float = 120.0
170
+ retry_after_cap_seconds: float = 30.0
171
+ delay_seconds_initial: float = 1.0
172
+ jitter_ratio: float = 0.25
173
+
174
+ def __post_init__(self) -> None:
175
+ if (
176
+ isinstance(self.attempts_max, bool)
177
+ or not isinstance(self.attempts_max, int)
178
+ or not 1 <= self.attempts_max <= 20
179
+ ):
180
+ raise ValueError("attempts_max must be an integer from 1 through 20")
181
+ for name in (
182
+ "total_wait_seconds_max",
183
+ "retry_after_cap_seconds",
184
+ "delay_seconds_initial",
185
+ "jitter_ratio",
186
+ ):
187
+ value = getattr(self, name)
188
+ if (
189
+ isinstance(value, bool)
190
+ or not isinstance(value, (int, float))
191
+ or not math.isfinite(value)
192
+ or value < 0
193
+ ):
194
+ raise ValueError(f"{name} must be a finite non-negative number")
195
+ if self.jitter_ratio > 1:
196
+ raise ValueError("jitter_ratio cannot exceed 1")
197
+
198
+ def next_delay_seconds(
199
+ self,
200
+ *,
201
+ attempts_made: int,
202
+ waited_seconds: float,
203
+ refusal: DbBackpressureRefusal,
204
+ rng: random.Random,
205
+ ) -> float | None:
206
+ """Return the sleep before the next attempt, or None when bounds are spent."""
207
+ if attempts_made >= self.attempts_max:
208
+ return None
209
+ if refusal.retry_after_seconds is not None:
210
+ base = min(self.retry_after_cap_seconds, refusal.retry_after_seconds)
211
+ else:
212
+ base = min(
213
+ self.retry_after_cap_seconds,
214
+ self.delay_seconds_initial * (2 ** (attempts_made - 1)),
215
+ )
216
+ delay = base * (1.0 + rng.uniform(0.0, self.jitter_ratio))
217
+ if waited_seconds + delay > self.total_wait_seconds_max:
218
+ return None
219
+ return delay
220
+
221
+
222
+ def request_permits_replay(method: str, headers: Mapping[str, str]) -> bool:
223
+ """A request may be replayed only when safe by method or carrying an idempotency key."""
224
+ if method.upper() in _REPLAY_SAFE_METHODS:
225
+ return True
226
+ return any(
227
+ name.lower() == "idempotency-key" and bool(str(value).strip())
228
+ for name, value in headers.items()
229
+ )
230
+
231
+
232
+ class DbBackpressureRetryExhaustedError(SynthError):
233
+ """The backend kept shedding DB work past the retry bounds.
234
+
235
+ Carries the last refusal's ``component``, ``work_class`` and ``retry_after``.
236
+ Not retryable again: callers already spent the bounded budget.
237
+ """
238
+
239
+ def __init__(
240
+ self,
241
+ *,
242
+ method: str,
243
+ path: str,
244
+ operation_id: str | None,
245
+ refusal: DbBackpressureRefusal,
246
+ attempts: int,
247
+ waited_seconds: float,
248
+ ) -> None:
249
+ self.method = method
250
+ self.path = path
251
+ self.refusal = refusal
252
+ self.attempts = attempts
253
+ self.waited_seconds = waited_seconds
254
+ detail = (
255
+ f"{method} {path}: backend refused DB work ({refusal.error_code}) on "
256
+ f"{attempts} attempts over {waited_seconds:.1f}s; last component="
257
+ f"{refusal.component} work_class={refusal.work_class} "
258
+ f"retry_after={refusal.retry_after_seconds} status={refusal.status} "
259
+ f"operator_action={refusal.operator_action}"
260
+ )
261
+ super().__init__(
262
+ detail,
263
+ failure=SynthFailure(
264
+ code=SynthErrorCode(DB_BACKPRESSURE_RETRY_EXHAUSTED_CODE),
265
+ category=SynthErrorCategory.TRANSIENT_SERVICE,
266
+ operation=operation_id,
267
+ request_id=None,
268
+ correlation_id=None,
269
+ retry=RetryDirective(
270
+ retryable=False,
271
+ retry_after_seconds=refusal.retry_after_seconds,
272
+ ),
273
+ status=refusal.status,
274
+ detail=detail,
275
+ reason=refusal.component,
276
+ ),
277
+ )
278
+
279
+ @property
280
+ def component(self) -> str | None:
281
+ return self.refusal.component
282
+
283
+ @property
284
+ def work_class(self) -> str | None:
285
+ return self.refusal.work_class
286
+
287
+ @property
288
+ def retry_after_seconds(self) -> float | None:
289
+ return self.refusal.retry_after_seconds
290
+
291
+
292
+ __all__ = [
293
+ "DB_BACKPRESSURE_ERROR_CODE",
294
+ "DbBackpressureRefusal",
295
+ "DbBackpressureRetryExhaustedError",
296
+ "DbBackpressureRetryPolicy",
297
+ "request_permits_replay",
298
+ "RetryPolicy",
299
+ "idempotency_key_from_request",
300
+ "retry_after_from_error",
301
+ "should_retry_failure",
302
+ ]
@@ -7,6 +7,7 @@ from __future__ import annotations
7
7
 
8
8
  import json
9
9
  import math
10
+ import random
10
11
  import time
11
12
  from collections.abc import Callable, Iterator, Mapping
12
13
  from dataclasses import dataclass, field
@@ -37,7 +38,12 @@ from synth_ai.core.errors import (
37
38
  )
38
39
  from synth_ai.core.http.request import HttpRequest
39
40
  from synth_ai.core.http.retry import (
41
+ DB_BACKPRESSURE_ERROR_CODE,
42
+ DbBackpressureRefusal,
43
+ DbBackpressureRetryExhaustedError,
44
+ DbBackpressureRetryPolicy,
40
45
  RetryPolicy,
46
+ request_permits_replay,
41
47
  retry_after_from_error,
42
48
  should_retry_failure,
43
49
  )
@@ -206,6 +212,37 @@ SEARCH_ID_HEADER = "x-synth-search-id"
206
212
  SEARCH_RESOURCE_KIND = "index_search"
207
213
 
208
214
 
215
+ def db_backpressure_refusal(response: httpx.Response) -> DbBackpressureRefusal | None:
216
+ """Decode the backend's typed DB backpressure refusal, or None for any other error."""
217
+ if response.status_code not in {429, 503}:
218
+ return None
219
+ try:
220
+ decoded = response.json()
221
+ except (json.JSONDecodeError, ValueError):
222
+ return None
223
+ if not isinstance(decoded, dict):
224
+ return None
225
+ detail = decoded.get("detail")
226
+ body = detail if isinstance(detail, dict) else decoded
227
+ if DB_BACKPRESSURE_ERROR_CODE not in (body.get("error_code"), body.get("failure_class")):
228
+ return None
229
+
230
+ def text(key: str) -> str | None:
231
+ value = body.get(key)
232
+ return value if isinstance(value, str) and value.strip() else None
233
+
234
+ retry_after = _retry_after_seconds(response, sources=(cast(JsonObject, body),))
235
+ return DbBackpressureRefusal(
236
+ status=response.status_code,
237
+ error_code=DB_BACKPRESSURE_ERROR_CODE,
238
+ component=text("component"),
239
+ work_class=text("work_class"),
240
+ retry_after_seconds=retry_after,
241
+ operator_action=text("operator_action"),
242
+ message=text("message") or "database work rejected before starting",
243
+ )
244
+
245
+
209
246
  def _created_search(
210
247
  response: httpx.Response, sources: tuple[JsonObject, ...]
211
248
  ) -> ResourceRef | None:
@@ -394,6 +431,12 @@ class HttpTransport:
394
431
  error_handler: ErrorHandler = raise_http_error
395
432
  exception_handler: ExceptionHandler = raise_transport_exception
396
433
  decode_error_handler: DecodeErrorHandler = raise_json_decode_error
434
+ db_backpressure_policy: DbBackpressureRetryPolicy = field(
435
+ default_factory=DbBackpressureRetryPolicy
436
+ )
437
+ sleep: Callable[[float], None] = time.sleep
438
+ monotonic: Callable[[], float] = time.monotonic
439
+ rng: random.Random = field(default_factory=random.Random, repr=False)
397
440
  client: httpx.Client = field(init=False, repr=False)
398
441
 
399
442
  def __post_init__(self) -> None:
@@ -420,19 +463,44 @@ class HttpTransport:
420
463
  timeout_seconds: float | None = None,
421
464
  operation_id: str | None = None,
422
465
  ) -> JsonValue:
423
- try:
424
- response = self.client.request(
425
- method,
426
- path,
427
- params=cast(Any, params),
428
- json=json_body,
429
- headers=headers,
430
- timeout=self.timeout_seconds if timeout_seconds is None else timeout_seconds,
466
+ replay_safe = request_permits_replay(method, {**self.headers, **(headers or {})})
467
+ attempts = 0
468
+ waited_seconds = 0.0
469
+ while True:
470
+ try:
471
+ response = self.client.request(
472
+ method,
473
+ path,
474
+ params=cast(Any, params),
475
+ json=json_body,
476
+ headers=headers,
477
+ timeout=self.timeout_seconds if timeout_seconds is None else timeout_seconds,
478
+ )
479
+ except httpx.TimeoutException as exc:
480
+ self.exception_handler(method, path, exc, operation_id)
481
+ except httpx.TransportError as exc:
482
+ self.exception_handler(method, path, exc, operation_id)
483
+ attempts += 1
484
+ refusal = db_backpressure_refusal(response) if replay_safe else None
485
+ if refusal is None:
486
+ break
487
+ delay = self.db_backpressure_policy.next_delay_seconds(
488
+ attempts_made=attempts,
489
+ waited_seconds=waited_seconds,
490
+ refusal=refusal,
491
+ rng=self.rng,
431
492
  )
432
- except httpx.TimeoutException as exc:
433
- self.exception_handler(method, path, exc, operation_id)
434
- except httpx.TransportError as exc:
435
- self.exception_handler(method, path, exc, operation_id)
493
+ if delay is None:
494
+ raise DbBackpressureRetryExhaustedError(
495
+ method=method,
496
+ path=path,
497
+ operation_id=operation_id,
498
+ refusal=refusal,
499
+ attempts=attempts,
500
+ waited_seconds=waited_seconds,
501
+ )
502
+ self.sleep(delay)
503
+ waited_seconds += delay
436
504
  if allow_not_found and response.status_code == 404:
437
505
  return None
438
506
  if response.is_error:
@@ -598,6 +666,7 @@ class HttpTransport:
598
666
 
599
667
 
600
668
  __all__ = [
669
+ "db_backpressure_refusal",
601
670
  "ErrorHandler",
602
671
  "ExceptionHandler",
603
672
  "DecodeErrorHandler",
@@ -3,6 +3,8 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import base64
6
+ import hashlib
7
+ import json
6
8
  import os
7
9
  import re
8
10
  from collections.abc import Iterable, Mapping
@@ -1018,6 +1020,22 @@ def _code_bundle_upload_payload(
1018
1020
  return payload
1019
1021
 
1020
1022
 
1023
+ def _code_bundle_upload_idempotency_key(project_id: str, payload: Mapping[str, Any]) -> str:
1024
+ """Content-derived replay identity for one code-bundle upload.
1025
+
1026
+ The backend route is convergent on identical content: ``upload_id`` hashes
1027
+ project+filename+bytes, and committing an identical tree is a no-op
1028
+ (``_apply_files_commit`` returns ``committed=False``). A replay with the same
1029
+ project and payload therefore cannot apply a second, different effect.
1030
+ """
1031
+ canonical = json.dumps(
1032
+ {"project_id": project_id, "payload": dict(payload)},
1033
+ sort_keys=True,
1034
+ separators=(",", ":"),
1035
+ )
1036
+ return "code-bundle-upload:" + hashlib.sha256(canonical.encode("utf-8")).hexdigest()
1037
+
1038
+
1021
1039
  @dataclass
1022
1040
  class ResearchSession(ManagedResearchRunAuthorityMixin):
1023
1041
  """Managed Research client implementation."""
@@ -3051,19 +3069,23 @@ class ResearchSession(ManagedResearchRunAuthorityMixin):
3051
3069
  default_branch: str = "main",
3052
3070
  metadata: Mapping[str, Any] | None = None,
3053
3071
  ) -> dict[str, Any]:
3072
+ payload = _code_bundle_upload_payload(
3073
+ bundle_path,
3074
+ filename=filename,
3075
+ content_type=content_type,
3076
+ source_kind=source_kind,
3077
+ commit_message=commit_message,
3078
+ default_branch=default_branch,
3079
+ metadata=metadata,
3080
+ )
3054
3081
  return _coerce_dict(
3055
3082
  self._request_json(
3056
3083
  "POST",
3057
3084
  f"/smr/projects/{project_id}/workspace/code-bundle:upload",
3058
- json_body=_code_bundle_upload_payload(
3059
- bundle_path,
3060
- filename=filename,
3061
- content_type=content_type,
3062
- source_kind=source_kind,
3063
- commit_message=commit_message,
3064
- default_branch=default_branch,
3065
- metadata=metadata,
3066
- ),
3085
+ json_body=payload,
3086
+ headers={
3087
+ "Idempotency-Key": _code_bundle_upload_idempotency_key(project_id, payload)
3088
+ },
3067
3089
  ),
3068
3090
  label="upload_project_code_bundle",
3069
3091
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: synth-ai
3
- Version: 0.22.2.dev688
3
+ Version: 0.22.2.dev689
4
4
  Summary: Python SDK, CLI and MCP servers for Synth Index search and Swarms
5
5
  Author-email: Synth AI <josh@usesynth.ai>
6
6
  License-Expression: MIT
@@ -1,135 +0,0 @@
1
- """Classified retry policy shared by sync and async transports.
2
-
3
- # See: testing/specifications/sdk/core_research_migration.md
4
- """
5
-
6
- from __future__ import annotations
7
-
8
- import math
9
- from dataclasses import dataclass
10
-
11
- from synth_ai.core.errors import ContractMismatchError, RuntimeUnavailableError, SynthError
12
- from synth_ai.core.http.request import HttpMethod, HttpRequest, OperationMetadata
13
-
14
-
15
- @dataclass(frozen=True, slots=True)
16
- class RetryPolicy:
17
- attempts_max: int = 3
18
- delay_seconds_initial: float = 0.5
19
- delay_seconds_max: float = 5.0
20
- retry_statuses: frozenset[int] = frozenset({408, 409, 429, 500, 502, 503, 504})
21
-
22
- def __post_init__(self) -> None:
23
- if (
24
- isinstance(self.attempts_max, bool)
25
- or not isinstance(self.attempts_max, int)
26
- or not 1 <= self.attempts_max <= 100
27
- ):
28
- raise ValueError("attempts_max must be an integer from 1 through 100")
29
- for delay in (self.delay_seconds_initial, self.delay_seconds_max):
30
- if (
31
- isinstance(delay, bool)
32
- or not isinstance(delay, (int, float))
33
- or not math.isfinite(delay)
34
- ):
35
- raise ValueError("retry delays must be finite numbers")
36
- if self.delay_seconds_initial < 0 or self.delay_seconds_max < 0:
37
- raise ValueError("retry delays must be non-negative")
38
- if self.delay_seconds_initial > self.delay_seconds_max:
39
- raise ValueError("delay_seconds_initial cannot exceed delay_seconds_max")
40
-
41
- def permits(
42
- self,
43
- operation: OperationMetadata,
44
- *,
45
- idempotency_key: str | None,
46
- ) -> bool:
47
- """Retry only metadata-declared idempotent operations.
48
-
49
- Unsafe methods additionally require an idempotency key.
50
- """
51
- if not operation.idempotent:
52
- return False
53
- if operation.method is HttpMethod.GET:
54
- return True
55
- return bool(idempotency_key)
56
-
57
- def delay_seconds(
58
- self,
59
- attempt_index: int,
60
- *,
61
- retry_after_seconds: float | None,
62
- ) -> float:
63
- """Deterministic exponential backoff capped by policy and Retry-After."""
64
- if attempt_index < 0:
65
- raise ValueError("attempt_index must be non-negative")
66
- exponential = min(
67
- self.delay_seconds_max,
68
- self.delay_seconds_initial * (2**attempt_index),
69
- )
70
- if retry_after_seconds is None:
71
- return exponential
72
- if not math.isfinite(retry_after_seconds) or retry_after_seconds < 0:
73
- raise ValueError("retry_after_seconds must be non-negative")
74
- return min(self.delay_seconds_max, max(exponential, retry_after_seconds))
75
-
76
-
77
- def idempotency_key_from_request(request: HttpRequest) -> str | None:
78
- """Extract an idempotency key from headers or JSON body fields."""
79
- values = [value for name, value in request.headers.items() if name.lower() == "idempotency-key"]
80
- if isinstance(request.body, dict):
81
- values.extend(
82
- request.body[name]
83
- for name in ("idempotency_key", "idempotency_key_run_create")
84
- if name in request.body
85
- )
86
- identities = set()
87
- for value in values:
88
- if not isinstance(value, str) or not value.strip():
89
- raise ValueError("idempotency identity must be a non-empty string")
90
- identities.add(value)
91
- if len(identities) > 1:
92
- raise ValueError("request contains conflicting idempotency identities")
93
- return next(iter(identities), None)
94
-
95
-
96
- def should_retry_failure(
97
- policy: RetryPolicy,
98
- request: HttpRequest,
99
- error: BaseException,
100
- ) -> bool:
101
- """Decide whether one failed execute attempt may be retried."""
102
- if isinstance(error, (ContractMismatchError, RuntimeUnavailableError)):
103
- return False
104
- if not policy.permits(
105
- request.operation,
106
- idempotency_key=idempotency_key_from_request(request),
107
- ):
108
- return False
109
- if isinstance(error, SynthError):
110
- if error.failure is not None:
111
- # The first-class directive is authoritative, including an explicit false.
112
- return error.failure.retry.retryable
113
- if error.retryable:
114
- return True
115
- status = None
116
- if error.failure is not None:
117
- status = error.failure.status
118
- if status is None and hasattr(error, "status"):
119
- status = error.status
120
- return isinstance(status, int) and status in policy.retry_statuses
121
- return False
122
-
123
-
124
- def retry_after_from_error(error: BaseException) -> float | None:
125
- if isinstance(error, SynthError):
126
- return error.retry_after_seconds
127
- return None
128
-
129
-
130
- __all__ = [
131
- "RetryPolicy",
132
- "idempotency_key_from_request",
133
- "retry_after_from_error",
134
- "should_retry_failure",
135
- ]