synth-ai 0.22.2.dev687__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.dev687/synth_ai.egg-info → synth_ai-0.22.2.dev689}/PKG-INFO +1 -1
  2. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/pyproject.toml +1 -1
  3. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/cli/index.py +1 -1
  4. synth_ai-0.22.2.dev689/synth_ai/core/http/retry.py +302 -0
  5. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/core/http/transport.py +81 -12
  6. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/registry.py +1 -0
  7. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/server.py +9 -0
  8. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/index.py +2 -22
  9. synth_ai-0.22.2.dev689/synth_ai/mcp/research/tools/local_files.py +5 -0
  10. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/runs.py +13 -0
  11. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/scientific_records.py +12 -1
  12. synth_ai-0.22.2.dev689/synth_ai/sdk/index/local_files.py +32 -0
  13. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/upload_session.py +1 -1
  14. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/__init__.py +8 -0
  15. synth_ai-0.22.2.dev689/synth_ai/sdk/research/contracts/forge_citations.py +29 -0
  16. synth_ai-0.22.2.dev689/synth_ai/sdk/research/contracts/forge_uploads.py +84 -0
  17. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/runtime_intent.py +40 -0
  18. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/status.py +81 -9
  19. synth_ai-0.22.2.dev689/synth_ai/sdk/research/contracts/swarm_history.py +167 -0
  20. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/operations.py +6 -0
  21. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/scientific_records.py +137 -0
  22. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/_client_helpers.py +24 -0
  23. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/client.py +54 -38
  24. synth_ai-0.22.2.dev689/synth_ai/sdk/research/session/directory_inputs.py +243 -0
  25. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/swarms.py +39 -0
  26. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689/synth_ai.egg-info}/PKG-INFO +1 -1
  27. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai.egg-info/SOURCES.txt +6 -0
  28. synth_ai-0.22.2.dev687/synth_ai/core/http/retry.py +0 -135
  29. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/LICENSE +0 -0
  30. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/MANIFEST.in +0 -0
  31. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/README.md +0 -0
  32. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/openapi/index-v1.json +0 -0
  33. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/openapi/messaging-v1.json +0 -0
  34. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/openapi/research-v1.json +0 -0
  35. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/setup.cfg +0 -0
  36. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/setup.py +0 -0
  37. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/README.md +0 -0
  38. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/__init__.py +0 -0
  39. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/__main__.py +0 -0
  40. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/cli/AGENTS.md +0 -0
  41. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/cli/README.md +0 -0
  42. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/cli/__init__.py +0 -0
  43. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/cli/__main__.py +0 -0
  44. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/cli/main.py +0 -0
  45. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/cli/research.py +0 -0
  46. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/cli/research_environments.py +0 -0
  47. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/cli/research_image_releases.py +0 -0
  48. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/cli/research_projects.py +0 -0
  49. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/cli/utils/__init__.py +0 -0
  50. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/cli/utils/research_launch.py +0 -0
  51. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/client.py +0 -0
  52. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/core/README.md +0 -0
  53. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/core/__init__.py +0 -0
  54. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/core/auth/__init__.py +0 -0
  55. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/core/auth/credentials.py +0 -0
  56. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/core/contracts/__init__.py +0 -0
  57. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/core/contracts/json_value.py +0 -0
  58. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/core/contracts/pagination.py +0 -0
  59. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/core/errors.py +0 -0
  60. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/core/http/__init__.py +0 -0
  61. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/core/http/async_transport.py +0 -0
  62. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/core/http/request.py +0 -0
  63. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/core/http/streaming.py +0 -0
  64. {synth_ai-0.22.2.dev687/synth_ai/mcp/research/tools → synth_ai-0.22.2.dev689/synth_ai/core}/local_files.py +0 -0
  65. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/core/research/__init__.py +0 -0
  66. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/core/utils/__init__.py +0 -0
  67. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/core/utils/env.py +0 -0
  68. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/core/utils/log_filter.py +0 -0
  69. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/core/utils/paths.py +0 -0
  70. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/core/utils/urls.py +0 -0
  71. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/README.md +0 -0
  72. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/__init__.py +0 -0
  73. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/__main__.py +0 -0
  74. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/objective_tools.py +0 -0
  75. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/request_models.py +0 -0
  76. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/__init__.py +0 -0
  77. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/approvals.py +0 -0
  78. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/artifacts.py +0 -0
  79. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/datasets.py +0 -0
  80. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/environments.py +0 -0
  81. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/exports.py +0 -0
  82. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/factories.py +0 -0
  83. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/factory_results.py +0 -0
  84. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/files.py +0 -0
  85. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/image_releases.py +0 -0
  86. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/index_qa.py +0 -0
  87. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/integrations.py +0 -0
  88. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/intern_program.py +0 -0
  89. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/launch_schemas.py +0 -0
  90. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/logs.py +0 -0
  91. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/models.py +0 -0
  92. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/outputs.py +0 -0
  93. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/policy_schemas.py +0 -0
  94. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/progress.py +0 -0
  95. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/project_data.py +0 -0
  96. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/projects.py +0 -0
  97. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/prs.py +0 -0
  98. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/readiness.py +0 -0
  99. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/repos.py +0 -0
  100. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/research_intern.py +0 -0
  101. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/resource_reads.py +0 -0
  102. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/resources.py +0 -0
  103. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/trained_models.py +0 -0
  104. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/usage.py +0 -0
  105. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/visuals.py +0 -0
  106. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/mcp/research/tools/workspace_inputs.py +0 -0
  107. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/pools.py +0 -0
  108. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/py.typed +0 -0
  109. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/README.md +0 -0
  110. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/__init__.py +0 -0
  111. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/AGENT_SETUP_PROMPT.md +0 -0
  112. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/README.md +0 -0
  113. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/__init__.py +0 -0
  114. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/agent_policy.py +0 -0
  115. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/artifacts.py +0 -0
  116. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/catalog.py +0 -0
  117. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/catalogue.py +0 -0
  118. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/classification.py +0 -0
  119. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/client.py +0 -0
  120. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/contracts.py +0 -0
  121. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/contributions.py +0 -0
  122. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/errors.py +0 -0
  123. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/forge_notices.py +0 -0
  124. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/intake.py +0 -0
  125. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/lifecycle.py +0 -0
  126. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/manifest.py +0 -0
  127. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/oauth.py +0 -0
  128. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/package.py +0 -0
  129. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/public.py +0 -0
  130. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/public_corpus.py +0 -0
  131. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/public_search.py +0 -0
  132. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/qa.py +0 -0
  133. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/qa_checks.py +0 -0
  134. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/qa_preflight.py +0 -0
  135. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/qa_reviews.py +0 -0
  136. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/research/__init__.py +0 -0
  137. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/research/build.py +0 -0
  138. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/research/capture.py +0 -0
  139. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/research/contracts.py +0 -0
  140. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/research/files.py +0 -0
  141. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/research/release.py +0 -0
  142. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/research_intake.py +0 -0
  143. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/retry.py +0 -0
  144. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/rights.py +0 -0
  145. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/scientific_provenance.py +0 -0
  146. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/scope_errors.py +0 -0
  147. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/scopes.py +0 -0
  148. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/search.py +0 -0
  149. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/submission.py +0 -0
  150. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/timeouts.py +0 -0
  151. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/transfer.py +0 -0
  152. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/usage_accounting.py +0 -0
  153. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/value.py +0 -0
  154. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/index/wallet_search.py +0 -0
  155. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/messaging/README.md +0 -0
  156. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/messaging/__init__.py +0 -0
  157. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/messaging/client.py +0 -0
  158. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/messaging/contracts.py +0 -0
  159. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/optimizers/__init__.py +0 -0
  160. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/optimizers/client.py +0 -0
  161. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/optimizers/contracts.py +0 -0
  162. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/pagination.py +0 -0
  163. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/__init__.py +0 -0
  164. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/_internal/__init__.py +0 -0
  165. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/_internal/crypto.py +0 -0
  166. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/_internal/env.py +0 -0
  167. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/_internal/urls.py +0 -0
  168. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/account.py +0 -0
  169. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/advanced.py +0 -0
  170. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/advanced_factories.py +0 -0
  171. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/advanced_swarms.py +0 -0
  172. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/artifacts.py +0 -0
  173. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/auth.py +0 -0
  174. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/client.py +0 -0
  175. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/container_pools/__init__.py +0 -0
  176. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/container_pools/api.py +0 -0
  177. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/container_pools/contracts.py +0 -0
  178. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/container_pools/operations.py +0 -0
  179. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/container_pools/packaging.py +0 -0
  180. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/_activity_wire.py +0 -0
  181. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/_environment_wire.py +0 -0
  182. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/_transcript_wire.py +0 -0
  183. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/_wire.py +0 -0
  184. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/activity.py +0 -0
  185. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/activity_items.py +0 -0
  186. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/actor_images.py +0 -0
  187. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/billing.py +0 -0
  188. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/canonical_usage.py +0 -0
  189. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/checkpoints.py +0 -0
  190. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/cloud_deployment_claims.py +0 -0
  191. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/cloud_deployments.py +0 -0
  192. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/common.py +0 -0
  193. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/dataset_revisions.py +0 -0
  194. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/deliveries.py +0 -0
  195. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/dev_environment_evidence.py +0 -0
  196. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/economics.py +0 -0
  197. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/environment_manifest.py +0 -0
  198. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/environments.py +0 -0
  199. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/evidence.py +0 -0
  200. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/experiment_evidence.py +0 -0
  201. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/factories.py +0 -0
  202. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/factory_evidence.py +0 -0
  203. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/factory_lenses.py +0 -0
  204. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/factory_operations.py +0 -0
  205. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/factory_role_receipts.py +0 -0
  206. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/failure.py +0 -0
  207. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/forge/__init__.py +0 -0
  208. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/forge/contracts.py +0 -0
  209. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/forge/evaluation.py +0 -0
  210. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/forge/exports.py +0 -0
  211. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/forge/operations.py +0 -0
  212. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/forge/records.py +0 -0
  213. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/forge/resources.py +0 -0
  214. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/forge/schema.py +0 -0
  215. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/image_releases.py +0 -0
  216. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/intern_authority.py +0 -0
  217. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/intern_blockers.py +0 -0
  218. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/intern_grants.py +0 -0
  219. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/intern_program.py +0 -0
  220. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/intern_resources.py +0 -0
  221. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/intern_usage.py +0 -0
  222. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/limit_evidence.py +0 -0
  223. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/local_execution_profile.py +0 -0
  224. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/native_attachment.py +0 -0
  225. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/native_result_outcome.py +0 -0
  226. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/operator_evidence.py +0 -0
  227. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/project.py +0 -0
  228. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/project_data.py +0 -0
  229. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/project_runtime.py +0 -0
  230. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/project_workspace.py +0 -0
  231. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/project_workspace_evidence.py +0 -0
  232. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/projects.py +0 -0
  233. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/promotions.py +0 -0
  234. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/research_intern.py +0 -0
  235. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/resource_reads.py +0 -0
  236. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/resource_settlement.py +0 -0
  237. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/run_authority.py +0 -0
  238. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/run_control.py +0 -0
  239. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/run_diagnostics.py +0 -0
  240. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/run_events.py +0 -0
  241. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/run_execution.py +0 -0
  242. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/run_launch.py +0 -0
  243. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/run_observability.py +0 -0
  244. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/run_state.py +0 -0
  245. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/run_timeline.py +0 -0
  246. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/runtime_image.py +0 -0
  247. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/runtime_stream.py +0 -0
  248. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/runtime_stream_snapshot.py +0 -0
  249. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/scientific_citations.py +0 -0
  250. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/scientific_integrity.py +0 -0
  251. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_actor_models.py +0 -0
  252. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_actor_policy_data.py +0 -0
  253. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_agent_harnesses.py +0 -0
  254. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_agent_kinds.py +0 -0
  255. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_agent_models.py +0 -0
  256. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_branching.py +0 -0
  257. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_credential_providers.py +0 -0
  258. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_environment_kinds.py +0 -0
  259. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_evidence_obligations.py +0 -0
  260. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_funding_sources.py +0 -0
  261. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_horizons.py +0 -0
  262. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_host_kinds.py +0 -0
  263. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_inference_providers.py +0 -0
  264. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_network_topology.py +0 -0
  265. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_providers.py +0 -0
  266. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_resource_kinds.py +0 -0
  267. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_roles.py +0 -0
  268. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_run_policy.py +0 -0
  269. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_runbooks.py +0 -0
  270. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_runnable_project.py +0 -0
  271. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_runtime_kinds.py +0 -0
  272. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_tool_providers.py +0 -0
  273. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/smr_work_modes.py +0 -0
  274. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/swarm_controls.py +0 -0
  275. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/swarm_rollouts.py +0 -0
  276. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/swarm_tasks.py +0 -0
  277. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/swarms.py +0 -0
  278. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/tag.py +0 -0
  279. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/traces.py +0 -0
  280. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/transcript.py +0 -0
  281. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/types.py +0 -0
  282. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/usage.py +0 -0
  283. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/visuals.py +0 -0
  284. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/wire_models.py +0 -0
  285. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/wire_models_README.md +0 -0
  286. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/work_products.py +0 -0
  287. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/contracts/workspaces.py +0 -0
  288. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/economics.py +0 -0
  289. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/efforts.py +0 -0
  290. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/environments.py +0 -0
  291. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/errors.py +0 -0
  292. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/events.py +0 -0
  293. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/experiments.py +0 -0
  294. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/facade.py +0 -0
  295. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/factories.py +0 -0
  296. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/factory_storage.py +0 -0
  297. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/image_releases.py +0 -0
  298. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/intern_authority.py +0 -0
  299. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/intern_blockers.py +0 -0
  300. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/intern_program.py +0 -0
  301. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/knowledge.py +0 -0
  302. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/models.py +0 -0
  303. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/project_data.py +0 -0
  304. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/project_deliveries.py +0 -0
  305. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/project_workspaces.py +0 -0
  306. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/projects.py +0 -0
  307. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/public.py +0 -0
  308. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/readouts.py +0 -0
  309. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/research_intern.py +0 -0
  310. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/resource_reads.py +0 -0
  311. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/secrets.py +0 -0
  312. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/README.md +0 -0
  313. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/__init__.py +0 -0
  314. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/_base.py +0 -0
  315. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/_run_authority_mixin.py +0 -0
  316. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/approvals.py +0 -0
  317. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/billing.py +0 -0
  318. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/cloud_deployments.py +0 -0
  319. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/config.py +0 -0
  320. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/cost.py +0 -0
  321. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/datasets.py +0 -0
  322. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/dev_environments.py +0 -0
  323. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/environments.py +0 -0
  324. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/exports.py +0 -0
  325. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/factories.py +0 -0
  326. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/factory_evidence.py +0 -0
  327. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/files.py +0 -0
  328. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/github.py +0 -0
  329. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/image_releases.py +0 -0
  330. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/images.py +0 -0
  331. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/logs.py +0 -0
  332. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/models.py +0 -0
  333. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/outputs.py +0 -0
  334. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/progress.py +0 -0
  335. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/project.py +0 -0
  336. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/projects.py +0 -0
  337. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/prs.py +0 -0
  338. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/readiness.py +0 -0
  339. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/repositories.py +0 -0
  340. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/runs.py +0 -0
  341. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/secrets.py +0 -0
  342. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/setup.py +0 -0
  343. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/tag.py +0 -0
  344. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/trained_models.py +0 -0
  345. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/transport.py +0 -0
  346. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/usage.py +0 -0
  347. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/work_products.py +0 -0
  348. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/session/workspace_inputs.py +0 -0
  349. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/surface_parity.py +0 -0
  350. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/traces.py +0 -0
  351. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/transport/__init__.py +0 -0
  352. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/transport/http.py +0 -0
  353. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/transport/pagination.py +0 -0
  354. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/version.py +0 -0
  355. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/visuals.py +0 -0
  356. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai/sdk/research/wiki.py +0 -0
  357. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai.egg-info/dependency_links.txt +0 -0
  358. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai.egg-info/entry_points.txt +0 -0
  359. {synth_ai-0.22.2.dev687 → synth_ai-0.22.2.dev689}/synth_ai.egg-info/requires.txt +0 -0
  360. {synth_ai-0.22.2.dev687 → 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.dev687
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.dev687"
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"}]
@@ -633,8 +633,8 @@ def contribution_status(contribution_id, revision_id, search_id, api_key, backen
633
633
  @_lifecycle_target_options
634
634
  def contribution_upload(draft_file, upload_spec_file, root, files, api_key, backend_url):
635
635
  """Prepare, transfer exactly the selected files, then finalize. Does not submit."""
636
- from synth_ai.mcp.research.tools.index import read_selected_files
637
636
  from synth_ai.sdk.index.contributions import ContributionDraft, ContributionUploadSpec
637
+ from synth_ai.sdk.index.local_files import read_selected_files
638
638
 
639
639
  selected: dict[str, str] = {}
640
640
  for item in files:
@@ -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",
@@ -109,6 +109,7 @@ _DEFAULT_REQUIRED_SCOPES_BY_TOOL_NAME: dict[str, tuple[str, ...]] = {
109
109
  "research_get_run": READ_SCOPES,
110
110
  "research_get_swarm_activity": READ_SCOPES,
111
111
  "research_get_swarm_configuration": READ_SCOPES,
112
+ "research_get_run_history": READ_SCOPES,
112
113
  "research_get_swarm_evidence": READ_SCOPES,
113
114
  "research_get_swarm_usage": READ_SCOPES,
114
115
  "research_get_run_execution": READ_SCOPES,
@@ -173,6 +173,7 @@ _STABLE_TOOL_NAMES = RESOURCE_READ_TOOL_NAMES | frozenset(
173
173
  "research_get_record",
174
174
  "research_get_record_citations",
175
175
  "research_get_native_attachments",
176
+ "research_verify_citations",
176
177
  "research_list_records",
177
178
  "research_get_operation_receipt",
178
179
  "research_record_events",
@@ -192,6 +193,7 @@ _STABLE_TOOL_NAMES = RESOURCE_READ_TOOL_NAMES | frozenset(
192
193
  "research_get_run_transcript",
193
194
  "research_get_swarm_activity",
194
195
  "research_get_swarm_configuration",
196
+ "research_get_run_history",
195
197
  "research_get_swarm_evidence",
196
198
  "research_get_swarm_status",
197
199
  "research_get_swarm_usage",
@@ -2015,6 +2017,13 @@ class ResearchMcpServer:
2015
2017
  result = client.runs.get(run_id, project_id=project_id)
2016
2018
  return asdict(result) if is_dataclass(result) else result
2017
2019
 
2020
+ def _tool_get_run_history(self, args: JSONDict) -> JSONDict:
2021
+ run_id = SwarmId(require_string(args, "run_id"))
2022
+ after = _optional_int_default(args, "after", 0)
2023
+ limit = _optional_int_default(args, "limit", 100)
2024
+ with self._core_client_from_args(args) as client:
2025
+ return client.swarms.history(run_id, after=after, limit=limit).to_wire()
2026
+
2018
2027
  def _tool_get_swarm_configuration(self, args: JSONDict) -> JSONDict:
2019
2028
  swarm_id = SwarmId(require_string(args, "run_id"))
2020
2029
  with self._core_client_from_args(args) as client:
@@ -7,8 +7,7 @@ Upload reads only explicitly listed files under an explicit root: this stdio ser
7
7
  runs on the caller's machine; the hosted server cannot read a client's disk.
8
8
  """
9
9
 
10
- import re
11
- from collections.abc import Callable, Mapping
10
+ from collections.abc import Callable
12
11
  from contextlib import AbstractContextManager
13
12
  from typing import Annotated
14
13
 
@@ -24,11 +23,11 @@ from synth_ai.mcp.research.tools.index_qa import (
24
23
  QA_READ_TOOL_NAMES,
25
24
  build_qa_tools,
26
25
  )
27
- from synth_ai.mcp.research.tools.local_files import SelectedFileReader
28
26
  from synth_ai.sdk.index.catalog import PublicSearchCapability
29
27
  from synth_ai.sdk.index.client import STATUS_POLL_TIMEOUT_SECONDS, IndexAPI, PublicIndexAPI
30
28
  from synth_ai.sdk.index.contracts import ContributionReference, Identifier, IndexContract
31
29
  from synth_ai.sdk.index.contributions import ContributionDraft, ContributionUploadSpec
30
+ from synth_ai.sdk.index.local_files import read_selected_files
32
31
  from synth_ai.sdk.index.public_search import (
33
32
  DEFAULT_PUBLIC_SEARCH_WAIT_SECONDS,
34
33
  PublicSearchDisabledError,
@@ -75,11 +74,6 @@ INDEX_WRITE_TOOL_NAMES: tuple[str, ...] = (
75
74
  )
76
75
  INDEX_TOOL_NAMES = frozenset(INDEX_READ_TOOL_NAMES + INDEX_WRITE_TOOL_NAMES)
77
76
 
78
- _UPLOAD_MAX_BYTES = 64 * 1024 * 1024
79
- _CREDENTIAL = re.compile(
80
- rb"(sk-[A-Za-z0-9_-]{20,}|AKIA[0-9A-Z]{16}|-----BEGIN [A-Z ]*PRIVATE KEY"
81
- rb"|ghp_[A-Za-z0-9]{30,}|xox[bpa]-[A-Za-z0-9-]{10,})"
82
- )
83
77
  _KEY = Field(min_length=1, max_length=128, pattern=r"^[a-zA-Z0-9_.-]+$")
84
78
 
85
79
 
@@ -158,20 +152,6 @@ class SubmitRequest(IndexContract):
158
152
  spec: ContributionSubmitSpec
159
153
 
160
154
 
161
- def read_selected_files(root: str, files: Mapping[str, str]) -> dict[str, bytes]:
162
- """Read exactly the listed regular files; reject escapes, symlinks and secrets."""
163
- content: dict[str, bytes] = {}
164
- total = 0
165
- with SelectedFileReader(root) as reader:
166
- for logical_path, relative in files.items():
167
- data = reader.read(relative, _UPLOAD_MAX_BYTES - total)
168
- total += len(data)
169
- if _CREDENTIAL.search(data):
170
- raise ValueError(f"{logical_path}: possible credential; remove it before upload")
171
- content[logical_path] = data
172
- return content
173
-
174
-
175
155
  _PUBLIC_SEARCH_DESCRIPTION = (
176
156
  "Search reviewed Synth Index Contributions (Index Search v0.2). Without an API key "
177
157
  "this is the free public search; mode is fast (synchronous) or deep (admitted, then "
@@ -0,0 +1,5 @@
1
+ """Compatibility import for the shared descriptor-confined file reader."""
2
+
3
+ from synth_ai.core.local_files import SelectedFileReader
4
+
5
+ __all__ = ["SelectedFileReader"]
@@ -821,6 +821,19 @@ def build_run_tools(server: Any) -> list[ToolDefinition]:
821
821
  ),
822
822
  handler=server._tool_get_run,
823
823
  ),
824
+ ToolDefinition(
825
+ name="research_get_run_history",
826
+ description="Read a bounded backend-authorized journal page with decision reasons and redacted inputs.",
827
+ input_schema=tool_schema(
828
+ {
829
+ "run_id": {"type": "string", "description": "Research run id."},
830
+ "after": {"type": "integer", "minimum": 0, "maximum": 2**64 - 1},
831
+ "limit": {"type": "integer", "minimum": 1, "maximum": 256},
832
+ },
833
+ required=["run_id"],
834
+ ),
835
+ handler=server._tool_get_run_history,
836
+ ),
824
837
  ToolDefinition(
825
838
  name="research_get_swarm_configuration",
826
839
  description=(
@@ -58,6 +58,14 @@ class EventArguments(Contract):
58
58
  limit: int = Field(default=100, ge=1, le=1000)
59
59
 
60
60
 
61
+ READ_DESCRIPTIONS = {
62
+ "research_verify_citations": (
63
+ "Check a retained record revision's external citations: retained, dangling, "
64
+ "denied, conflict, unavailable or refused. Read-only."
65
+ ),
66
+ }
67
+
68
+
61
69
  def build_scientific_record_tools(client_factory):
62
70
  def execution_writer(arguments):
63
71
  selected = ExecutionWriteArguments.model_validate(arguments)
@@ -159,6 +167,7 @@ def build_scientific_record_tools(client_factory):
159
167
  )
160
168
  for name, model, method in (
161
169
  ("research_get_record", ReadArguments, "get"),
170
+ ("research_verify_citations", ReadArguments, "verify_citations"),
162
171
  ("research_list_records", ListArguments, "list"),
163
172
  ("research_get_operation_receipt", ReceiptArguments, "receipt"),
164
173
  ("research_record_events", EventArguments, "events"),
@@ -180,7 +189,9 @@ def build_scientific_record_tools(client_factory):
180
189
  tools.append(
181
190
  ToolDefinition(
182
191
  name=name,
183
- description="Read scoped retained scientific evidence.",
192
+ description=READ_DESCRIPTIONS.get(
193
+ name, "Read scoped retained scientific evidence."
194
+ ),
184
195
  input_schema=model.model_json_schema(),
185
196
  handler=reader(model, method),
186
197
  required_scopes=READ_SCOPES,
@@ -0,0 +1,32 @@
1
+ """Explicit, bounded local bytes shared by Index SDK, CLI and MCP uploads.
2
+
3
+ # See: synth_ai/core/local_files.py (descriptor confinement)
4
+ """
5
+
6
+ import re
7
+ from collections.abc import Mapping
8
+
9
+ from synth_ai.core.local_files import SelectedFileReader
10
+
11
+ _UPLOAD_MAX_BYTES = 64 * 1024 * 1024
12
+ _CREDENTIAL = re.compile(
13
+ rb"(sk-[A-Za-z0-9_-]{20,}|AKIA[0-9A-Z]{16}|-----BEGIN [A-Z ]*PRIVATE KEY"
14
+ rb"|ghp_[A-Za-z0-9]{30,}|xox[bpa]-[A-Za-z0-9-]{10,})"
15
+ )
16
+
17
+
18
+ def read_selected_files(root: str, files: Mapping[str, str]) -> dict[str, bytes]:
19
+ """Read exactly the listed regular files; reject escapes, symlinks and secrets."""
20
+ content: dict[str, bytes] = {}
21
+ total = 0
22
+ with SelectedFileReader(root) as reader:
23
+ for logical_path, relative in files.items():
24
+ data = reader.read(relative, _UPLOAD_MAX_BYTES - total)
25
+ total += len(data)
26
+ if _CREDENTIAL.search(data):
27
+ raise ValueError(f"{logical_path}: possible credential; remove it before upload")
28
+ content[logical_path] = data
29
+ return content
30
+
31
+
32
+ __all__ = ["read_selected_files"]
@@ -30,8 +30,8 @@ from urllib.parse import urlsplit
30
30
  import httpx
31
31
  from pydantic import ValidationError
32
32
 
33
- from synth_ai.mcp.research.tools.index import read_selected_files
34
33
  from synth_ai.sdk.index.contributions import ContributionUploadPrepared
34
+ from synth_ai.sdk.index.local_files import read_selected_files
35
35
  from synth_ai.sdk.index.transfer import _transfer_content
36
36
 
37
37
  _ACCEPTED = (200, 201, 204)