imbi-api 2.41.0__tar.gz → 2.43.0__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 (426) hide show
  1. {imbi_api-2.41.0 → imbi_api-2.43.0}/PKG-INFO +3 -4
  2. {imbi_api-2.41.0 → imbi_api-2.43.0}/pyproject.toml +3 -4
  3. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/agent_tasks/__init__.py +2 -0
  4. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/agent_tasks/store.py +88 -0
  5. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/agent_api_routes.py +1 -0
  6. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/seed.py +7 -0
  7. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/commit_sync/service.py +35 -3
  8. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/agent_tasks.py +43 -0
  9. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/agents.py +90 -25
  10. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/harness.py +48 -40
  11. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/project_deployments.py +505 -82
  12. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/projects.py +95 -23
  13. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/secret_broker.py +10 -3
  14. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/harness/marketplaces.py +44 -67
  15. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/identity/state.py +3 -4
  16. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/maintenance/registry.py +4 -1
  17. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/secret_broker/__init__.py +1 -4
  18. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/secret_broker/broker.py +27 -58
  19. imbi_api-2.43.0/src/imbi/api/secret_broker/providers.py +287 -0
  20. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_agent_platform.py +2 -0
  21. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_agent_task_log.py +32 -0
  22. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_agent_task_relations.py +14 -2
  23. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_agent_tasks.py +132 -3
  24. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_agents.py +119 -12
  25. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_github_app_repository.py +12 -14
  26. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_project_deployments.py +958 -15
  27. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_projects.py +161 -0
  28. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_secret_broker.py +1 -1
  29. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/secret_support.py +22 -0
  30. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_agent_platform.py +105 -17
  31. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_commit_sync_service.py +49 -0
  32. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_projects_drift_ranges.py +56 -1
  33. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_secret_broker.py +0 -3
  34. imbi_api-2.43.0/tests/test_secret_github_app.py +250 -0
  35. imbi_api-2.43.0/tests/test_secret_providers.py +137 -0
  36. imbi_api-2.41.0/src/imbi/api/auth/oauth.py +0 -496
  37. imbi_api-2.41.0/src/imbi/api/secret_broker/github_app.py +0 -747
  38. imbi_api-2.41.0/src/imbi/api/secret_broker/providers.py +0 -765
  39. imbi_api-2.41.0/tests/auth/test_oauth.py +0 -1110
  40. imbi_api-2.41.0/tests/test_secret_github_app.py +0 -749
  41. imbi_api-2.41.0/tests/test_secret_github_app_live.py +0 -156
  42. imbi_api-2.41.0/tests/test_secret_providers.py +0 -376
  43. {imbi_api-2.41.0 → imbi_api-2.43.0}/.gitignore +0 -0
  44. {imbi_api-2.41.0 → imbi_api-2.43.0}/README.md +0 -0
  45. {imbi_api-2.41.0 → imbi_api-2.43.0}/moon.yml +0 -0
  46. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/README.md +0 -0
  47. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/__init__.py +0 -0
  48. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/agent_tasks/log.py +0 -0
  49. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/agent_tasks/schemata.toml +0 -0
  50. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/agent_tasks/sweeper.py +0 -0
  51. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/agent_tools.py +0 -0
  52. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/app.py +0 -0
  53. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/__init__.py +0 -0
  54. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/agent_api.py +0 -0
  55. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/authorization_codes.py +0 -0
  56. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/autonomous.py +0 -0
  57. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/internal_services.py +0 -0
  58. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/local_auth.py +0 -0
  59. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/login_providers.py +0 -0
  60. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/membership.py +0 -0
  61. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/models.py +0 -0
  62. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/oauth_clients.py +0 -0
  63. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/organizations.py +0 -0
  64. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/password.py +0 -0
  65. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/permissions.py +0 -0
  66. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/principals.py +0 -0
  67. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/sessions.py +0 -0
  68. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/tokens.py +0 -0
  69. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/totp.py +0 -0
  70. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/backfill_embeddings.py +0 -0
  71. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/blueprint_attributes.py +0 -0
  72. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/blueprint_compliance.py +0 -0
  73. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/commit_pushed_at_repair.py +0 -0
  74. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/commit_sync/__init__.py +0 -0
  75. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/commit_sync/queue.py +0 -0
  76. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/component_facts.py +0 -0
  77. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/deployment_status_repair.py +0 -0
  78. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/deployment_sweeper.py +0 -0
  79. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/deployment_sync/__init__.py +0 -0
  80. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/deployment_sync/queue.py +0 -0
  81. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/deployment_sync/service.py +0 -0
  82. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/documents/__init__.py +0 -0
  83. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/documents/read_sweeper.py +0 -0
  84. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/domain/__init__.py +0 -0
  85. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/domain/models.py +0 -0
  86. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/domain/scoring.py +0 -0
  87. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/drift.py +0 -0
  88. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/email/__init__.py +0 -0
  89. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/email/client.py +0 -0
  90. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/email/dependencies.py +0 -0
  91. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/email/models.py +0 -0
  92. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/email/templates/base.html +0 -0
  93. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/email/templates/base.txt +0 -0
  94. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/email/templates/password_reset.html +0 -0
  95. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/email/templates/password_reset.txt +0 -0
  96. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/email/templates/welcome.html +0 -0
  97. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/email/templates/welcome.txt +0 -0
  98. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/email/templates.py +0 -0
  99. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/__init__.py +0 -0
  100. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/_credentials.py +0 -0
  101. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/_document_events.py +0 -0
  102. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/_document_history.py +0 -0
  103. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/_document_reads.py +0 -0
  104. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/_document_scope.py +0 -0
  105. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/_helpers.py +0 -0
  106. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/_json_fields.py +0 -0
  107. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/_pagination.py +0 -0
  108. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/_request_urls.py +0 -0
  109. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/_search_index.py +0 -0
  110. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/admin.py +0 -0
  111. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/admin_plugins.py +0 -0
  112. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/agent_platform.py +0 -0
  113. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/agent_task_harness.py +0 -0
  114. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/agent_usage.py +0 -0
  115. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/ai_models.py +0 -0
  116. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/ai_providers.py +0 -0
  117. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/api_keys.py +0 -0
  118. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/auth.py +0 -0
  119. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/auth_providers.py +0 -0
  120. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/blueprints.py +0 -0
  121. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/client_credentials.py +0 -0
  122. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/comments.py +0 -0
  123. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/components.py +0 -0
  124. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/dashboard.py +0 -0
  125. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/document_analytics.py +0 -0
  126. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/document_likes.py +0 -0
  127. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/document_presence.py +0 -0
  128. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/document_templates.py +0 -0
  129. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/document_versions.py +0 -0
  130. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/documents.py +0 -0
  131. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/environments.py +0 -0
  132. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/events.py +0 -0
  133. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/graph_query.py +0 -0
  134. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/iggy_connectors.py +0 -0
  135. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/integrations.py +0 -0
  136. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/link_definitions.py +0 -0
  137. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/local_auth.py +0 -0
  138. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/maintenance.py +0 -0
  139. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/mcp_servers.py +0 -0
  140. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/mfa.py +0 -0
  141. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/oauth_metadata.py +0 -0
  142. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/operations_log.py +0 -0
  143. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/organizations.py +0 -0
  144. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/plugin_edges.py +0 -0
  145. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/plugin_entities.py +0 -0
  146. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/plugins.py +0 -0
  147. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/project_analysis.py +0 -0
  148. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/project_commit_sync.py +0 -0
  149. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/project_configuration.py +0 -0
  150. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/project_incidents.py +0 -0
  151. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/project_integrations.py +0 -0
  152. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/project_lifecycle.py +0 -0
  153. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/project_logs.py +0 -0
  154. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/project_plugins.py +0 -0
  155. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/project_pr_sync.py +0 -0
  156. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/project_types.py +0 -0
  157. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/prompts.py +0 -0
  158. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/pull_requests.py +0 -0
  159. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/releases.py +0 -0
  160. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/roles.py +0 -0
  161. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/sa_api_keys.py +0 -0
  162. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/scoring.py +0 -0
  163. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/scoring_policies.py +0 -0
  164. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/search.py +0 -0
  165. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/service_accounts.py +0 -0
  166. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/status.py +0 -0
  167. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/tags.py +0 -0
  168. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/teams.py +0 -0
  169. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/uploads.py +0 -0
  170. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/user_activity.py +0 -0
  171. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/users.py +0 -0
  172. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/webhooks.py +0 -0
  173. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/workflow_runs.py +0 -0
  174. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/workflows.py +0 -0
  175. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/entrypoint.py +0 -0
  176. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/graph_ids.py +0 -0
  177. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/graph_sql.py +0 -0
  178. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/harness/__init__.py +0 -0
  179. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/harness/auth.py +0 -0
  180. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/harness/egress.py +0 -0
  181. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/harness/metrics.py +0 -0
  182. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/harness/platform.py +0 -0
  183. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/harness/schemata.toml +0 -0
  184. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/harness/store.py +0 -0
  185. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/harness/sweeper.py +0 -0
  186. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/harness/tokens.py +0 -0
  187. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/identity/__init__.py +0 -0
  188. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/identity/attribution.py +0 -0
  189. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/identity/endpoints.py +0 -0
  190. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/identity/errors.py +0 -0
  191. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/identity/flows.py +0 -0
  192. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/identity/host_integration.py +0 -0
  193. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/identity/models.py +0 -0
  194. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/identity/repository.py +0 -0
  195. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/identity/resolution.py +0 -0
  196. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/identity/sweeper.py +0 -0
  197. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/iggy_metrics.py +0 -0
  198. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/lifespans.py +0 -0
  199. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/llm/__init__.py +0 -0
  200. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/llm/dependencies.py +0 -0
  201. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/maintenance/__init__.py +0 -0
  202. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/maintenance/log.py +0 -0
  203. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/maintenance/operations.py +0 -0
  204. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/maintenance/state.py +0 -0
  205. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/maintenance/worker.py +0 -0
  206. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/mcp_test.py +0 -0
  207. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/middleware/__init__.py +0 -0
  208. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/middleware/rate_limit.py +0 -0
  209. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/models.py +0 -0
  210. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/openapi.py +0 -0
  211. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/orphan_releases.py +0 -0
  212. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/plugins/__init__.py +0 -0
  213. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/plugins/assignment_writer.py +0 -0
  214. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/plugins/assignments.py +0 -0
  215. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/plugins/credentials.py +0 -0
  216. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/plugins/installer.py +0 -0
  217. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/plugins/lifecycle.py +0 -0
  218. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/plugins/lifecycle_dispatch.py +0 -0
  219. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/plugins/reload.py +0 -0
  220. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/plugins/resolution.py +0 -0
  221. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/plugins/schemas.py +0 -0
  222. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/pr_sync/__init__.py +0 -0
  223. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/pr_sync/queue.py +0 -0
  224. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/pr_sync/service.py +0 -0
  225. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/prompts/__init__.py +0 -0
  226. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/prompts/release_notes_system.md +0 -0
  227. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/prompts/seed.py +0 -0
  228. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/py.typed +0 -0
  229. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/relationships.py +0 -0
  230. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/release_promote/__init__.py +0 -0
  231. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/release_promote/queue.py +0 -0
  232. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/release_promote/service.py +0 -0
  233. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/sbom.py +0 -0
  234. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/scoring/__init__.py +0 -0
  235. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/scoring/queue.py +0 -0
  236. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/secret_broker/audit.py +0 -0
  237. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/secret_broker/schemata.toml +0 -0
  238. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/secret_broker/store.py +0 -0
  239. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/settings.py +0 -0
  240. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/storage/__init__.py +0 -0
  241. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/storage/client.py +0 -0
  242. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/storage/dependencies.py +0 -0
  243. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/storage/thumbnails.py +0 -0
  244. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/storage/validation.py +0 -0
  245. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/user_document_orgs.py +0 -0
  246. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/workflows/__init__.py +0 -0
  247. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/workflows/runs.py +0 -0
  248. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/workflows/runtime_schemata.toml +0 -0
  249. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/workflows/schemata.toml +0 -0
  250. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/workflows/store.py +0 -0
  251. {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/workflows/sweeper.py +0 -0
  252. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/__init__.py +0 -0
  253. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/auth/__init__.py +0 -0
  254. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/auth/test_authentication.py +0 -0
  255. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/auth/test_authorization.py +0 -0
  256. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/auth/test_encryption.py +0 -0
  257. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/auth/test_internal_services.py +0 -0
  258. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/auth/test_login_providers.py +0 -0
  259. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/auth/test_membership.py +0 -0
  260. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/auth/test_oauth_server.py +0 -0
  261. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/auth/test_organizations.py +0 -0
  262. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/auth/test_permissions.py +0 -0
  263. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/auth/test_seed.py +0 -0
  264. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/auth/test_sessions.py +0 -0
  265. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/auth/test_totp.py +0 -0
  266. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/clickhouse/__init__.py +0 -0
  267. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/clickhouse/test_client.py +0 -0
  268. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/clickhouse/test_init.py +0 -0
  269. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/clickhouse/test_privacy.py +0 -0
  270. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/email/__init__.py +0 -0
  271. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/email/test_audit_clickhouse.py +0 -0
  272. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/email/test_client.py +0 -0
  273. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/email/test_init.py +0 -0
  274. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/email/test_integration.py +0 -0
  275. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/email/test_models.py +0 -0
  276. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/email/test_templates.py +0 -0
  277. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/__init__.py +0 -0
  278. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_admin.py +0 -0
  279. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_admin_plugins.py +0 -0
  280. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_agent_api.py +0 -0
  281. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_agent_task_actions.py +0 -0
  282. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_agent_task_harness.py +0 -0
  283. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_agent_usage.py +0 -0
  284. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_ai_models.py +0 -0
  285. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_ai_providers.py +0 -0
  286. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_api_keys.py +0 -0
  287. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_auth.py +0 -0
  288. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_auth_providers.py +0 -0
  289. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_blueprints.py +0 -0
  290. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_client_credentials.py +0 -0
  291. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_comments.py +0 -0
  292. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_components.py +0 -0
  293. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_credentials.py +0 -0
  294. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_cross_org_reads.py +0 -0
  295. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_dashboard.py +0 -0
  296. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_document_analytics.py +0 -0
  297. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_document_likes.py +0 -0
  298. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_document_presence.py +0 -0
  299. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_document_reads.py +0 -0
  300. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_document_reads_sql.py +0 -0
  301. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_document_templates.py +0 -0
  302. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_document_versions.py +0 -0
  303. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_documents.py +0 -0
  304. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_environments.py +0 -0
  305. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_events.py +0 -0
  306. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_graph_query.py +0 -0
  307. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_harness.py +0 -0
  308. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_harness_egress.py +0 -0
  309. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_harness_secrets.py +0 -0
  310. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_iggy_connectors.py +0 -0
  311. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_init.py +0 -0
  312. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_integrations.py +0 -0
  313. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_json_fields.py +0 -0
  314. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_link_definitions.py +0 -0
  315. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_local_auth.py +0 -0
  316. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_maintenance.py +0 -0
  317. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_mcp_servers.py +0 -0
  318. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_mfa.py +0 -0
  319. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_operations_log.py +0 -0
  320. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_organizations.py +0 -0
  321. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_pagination.py +0 -0
  322. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_plugin_label_validation.py +0 -0
  323. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_plugins.py +0 -0
  324. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_project_analysis.py +0 -0
  325. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_project_commit_sync.py +0 -0
  326. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_project_configuration.py +0 -0
  327. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_project_deployments_autonomous.py +0 -0
  328. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_project_incidents.py +0 -0
  329. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_project_integrations.py +0 -0
  330. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_project_lifecycle.py +0 -0
  331. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_project_logs.py +0 -0
  332. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_project_plugins.py +0 -0
  333. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_project_response_surfacing.py +0 -0
  334. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_project_types.py +0 -0
  335. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_projects_helpers.py +0 -0
  336. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_prompt_decisions.py +0 -0
  337. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_prompts.py +0 -0
  338. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_pull_requests.py +0 -0
  339. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_releases.py +0 -0
  340. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_request_urls.py +0 -0
  341. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_roles.py +0 -0
  342. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_sa_api_keys.py +0 -0
  343. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_scoring.py +0 -0
  344. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_scoring_policies.py +0 -0
  345. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_search.py +0 -0
  346. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_search_index.py +0 -0
  347. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_service_accounts.py +0 -0
  348. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_service_writeback.py +0 -0
  349. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_status.py +0 -0
  350. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_tags.py +0 -0
  351. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_teams.py +0 -0
  352. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_uploads.py +0 -0
  353. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_user_activity.py +0 -0
  354. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_users.py +0 -0
  355. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_webhooks.py +0 -0
  356. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_workflow_run_history.py +0 -0
  357. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_workflow_runs.py +0 -0
  358. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_workflow_triggers.py +0 -0
  359. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_workflows.py +0 -0
  360. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/fixtures/marketplaces/claude-code.json +0 -0
  361. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/fixtures/sbom/npm-realistic.json +0 -0
  362. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/fixtures/sbom/pypi-realistic.json +0 -0
  363. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/fixtures/sbom/tiny.json +0 -0
  364. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/identity/__init__.py +0 -0
  365. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/identity/test_endpoints.py +0 -0
  366. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/identity/test_errors.py +0 -0
  367. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/identity/test_flows.py +0 -0
  368. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/identity/test_host_integration.py +0 -0
  369. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/identity/test_repository.py +0 -0
  370. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/identity/test_resolution.py +0 -0
  371. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/identity/test_state.py +0 -0
  372. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/identity/test_sweeper.py +0 -0
  373. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/middleware/test_rate_limit.py +0 -0
  374. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/storage/__init__.py +0 -0
  375. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/storage/test_client.py +0 -0
  376. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/storage/test_init.py +0 -0
  377. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/storage/test_thumbnails.py +0 -0
  378. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/storage/test_validation.py +0 -0
  379. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/support.py +0 -0
  380. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_agent_task_events_e2e.py +0 -0
  381. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_agent_tools.py +0 -0
  382. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_app.py +0 -0
  383. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_assignment_writer.py +0 -0
  384. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_auth_settings.py +0 -0
  385. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_backfill_embeddings.py +0 -0
  386. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_blueprint_attributes.py +0 -0
  387. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_blueprint_compliance.py +0 -0
  388. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_blueprints.py +0 -0
  389. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_commit_pushed_at_repair.py +0 -0
  390. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_commit_sync_queue.py +0 -0
  391. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_component_facts.py +0 -0
  392. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_deployment_status_repair.py +0 -0
  393. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_deployment_sweeper.py +0 -0
  394. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_deployment_sync_queue.py +0 -0
  395. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_deployment_sync_service.py +0 -0
  396. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_drift.py +0 -0
  397. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_entrypoint.py +0 -0
  398. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_graph_ids.py +0 -0
  399. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_graph_sql.py +0 -0
  400. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_identity_attribution.py +0 -0
  401. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_iggy_metrics.py +0 -0
  402. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_init.py +0 -0
  403. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_installer.py +0 -0
  404. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_lifecycle_dispatch.py +0 -0
  405. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_lifespans.py +0 -0
  406. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_maintenance_log.py +0 -0
  407. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_maintenance_operations.py +0 -0
  408. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_maintenance_state.py +0 -0
  409. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_maintenance_worker.py +0 -0
  410. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_models.py +0 -0
  411. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_openapi.py +0 -0
  412. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_orphan_releases.py +0 -0
  413. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_plugin_schemas.py +0 -0
  414. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_plugins.py +0 -0
  415. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_plugins_assignments.py +0 -0
  416. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_plugins_credentials.py +0 -0
  417. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_pr_sync_queue.py +0 -0
  418. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_pr_sync_service.py +0 -0
  419. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_prompt_seed.py +0 -0
  420. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_pull_requests_pending_deploy.py +0 -0
  421. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_release_promote_service.py +0 -0
  422. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_sbom.py +0 -0
  423. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_scoring_queue.py +0 -0
  424. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_scoring_triggers.py +0 -0
  425. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_settings.py +0 -0
  426. {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_workflow_reconcile.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: imbi-api
3
- Version: 2.41.0
3
+ Version: 2.43.0
4
4
  Summary: Imbi is a DevOps Service Management Platform designed to provide an efficient way to manage a large environment that contains many services and applications.
5
5
  Author-email: Alex Campbell <alexc@aweber.com>, Dave Shawley <daves@aweber.com>, "Gavin M. Roy" <gavinr@aweber.com>
6
6
  License-Expression: BSD-3-Clause
@@ -19,15 +19,14 @@ Requires-Dist: cyclonedx-python-lib<12,>=11.7
19
19
  Requires-Dist: fastapi
20
20
  Requires-Dist: filetype
21
21
  Requires-Dist: httpx<1,>=0.28.1
22
- Requires-Dist: imbi-common[databases,llm,mcp,sentry,server]==2.41.0
23
- Requires-Dist: imbi-plugin-github==2.41.0
22
+ Requires-Dist: imbi-common[databases,llm,mcp,sentry,server]==2.43.0
24
23
  Requires-Dist: jinja2
25
24
  Requires-Dist: jsonpatch>=1.33
26
25
  Requires-Dist: limits<6,>=5
27
26
  Requires-Dist: mcp>=1.26.0
28
27
  Requires-Dist: nanoid>=2.0.0
29
28
  Requires-Dist: orjson>=3.11.6
30
- Requires-Dist: pillow
29
+ Requires-Dist: pillow>=13.0.0
31
30
  Requires-Dist: pydantic[email]
32
31
  Requires-Dist: pyotp>=2.9.0
33
32
  Requires-Dist: python-multipart>=0.0.26
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "imbi-api"
3
- version = "2.41.0"
3
+ version = "2.43.0"
4
4
  description = "Imbi is a DevOps Service Management Platform designed to provide an efficient way to manage a large environment that contains many services and applications."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.14"
@@ -19,7 +19,7 @@ classifiers = [
19
19
  "Programming Language :: Python :: 3.14",
20
20
  ]
21
21
  dependencies = [
22
- "Pillow",
22
+ "Pillow>=13.0.0",
23
23
  "aioboto3>=15.5.0",
24
24
  "aiohttp",
25
25
  "argon2-cffi>=25.1.0",
@@ -28,8 +28,7 @@ dependencies = [
28
28
  "fastapi",
29
29
  "filetype",
30
30
  "httpx>=0.28.1,<1",
31
- "imbi-common[databases,llm,mcp,sentry,server]==2.41.0",
32
- "imbi-plugin-github==2.41.0",
31
+ "imbi-common[databases,llm,mcp,sentry,server]==2.43.0",
33
32
  "jinja2",
34
33
  "jsonpatch>=1.33",
35
34
  "limits>=5,<6",
@@ -42,6 +42,7 @@ from imbi.api.agent_tasks.store import (
42
42
  TaskArchived,
43
43
  TaskClosed,
44
44
  TaskNotFound,
45
+ TaskOpen,
45
46
  TaskStore,
46
47
  Usage,
47
48
  )
@@ -124,6 +125,7 @@ __all__ = [
124
125
  'TaskArchived',
125
126
  'TaskClosed',
126
127
  'TaskNotFound',
128
+ 'TaskOpen',
127
129
  'TaskStore',
128
130
  'Usage',
129
131
  'create_pool',
@@ -142,6 +142,10 @@ class TaskClosed(ValueError):
142
142
  """The task is closed. A closed task does not change."""
143
143
 
144
144
 
145
+ class TaskOpen(ValueError):
146
+ """The task, or a task that it delegated to, is not closed."""
147
+
148
+
145
149
  class CancelPending(ValueError):
146
150
  """A cancel waits for the harness; the control cannot change."""
147
151
 
@@ -454,10 +458,24 @@ class TaskStore:
454
458
  Return the task and ``True``. When a concurrent request made a
455
459
  task with the same idempotency key first, return that task and
456
460
  ``False``. The rollback also returns the short id to the counter.
461
+
462
+ Raises:
463
+ TaskClosed: The origin is a task that is closed or gone.
464
+
457
465
  """
458
466
  task_id = uuid.uuid4()
459
467
  try:
460
468
  async with self._pool.connection() as conn, conn.transaction():
469
+ if task.origin_kind == 'task':
470
+ # The lock conflicts with the lock in delete, so a
471
+ # parent cannot go while its child is made.
472
+ cursor = await conn.execute(
473
+ 'SELECT 1 FROM agent_runtime.tasks WHERE id = %s::uuid'
474
+ " AND status <> 'closed' FOR KEY SHARE",
475
+ (task.origin_id,),
476
+ )
477
+ if await cursor.fetchone() is None:
478
+ raise TaskClosed(task.origin_id)
461
479
  short_id = await _next_short_id(conn, task.organization_id)
462
480
  await conn.execute(
463
481
  'INSERT INTO agent_runtime.tasks (id, organization_id,'
@@ -709,6 +727,76 @@ class TaskStore:
709
727
  )
710
728
  return True
711
729
 
730
+ async def delete(self, organization_id: str, short_id: str) -> Row:
731
+ """Delete a closed task and return its row.
732
+
733
+ Its events, sessions, requests, budget ledger, and harness rows
734
+ go by cascade; its dependency and project rows go here. The
735
+ secret resolutions and the ClickHouse rows (archived events,
736
+ usage) stay, for the security and cost history.
737
+
738
+ Raises:
739
+ TaskNotFound: No such task.
740
+ TaskOpen: The task, or a child or chain task, is not closed,
741
+ the log of the task is not archived, its harness chain
742
+ is not done, or it has open secret resolutions.
743
+
744
+ """
745
+ async with self._pool.connection() as conn, conn.transaction():
746
+ task = await _fetch_one(
747
+ conn,
748
+ 'SELECT * FROM agent_runtime.tasks'
749
+ ' WHERE organization_id = %s AND short_id = %s FOR UPDATE',
750
+ (organization_id, short_id),
751
+ )
752
+ if task is None:
753
+ raise TaskNotFound(short_id)
754
+ # The cascade would remove events and ledger rows that the
755
+ # sweep has not yet put in ClickHouse.
756
+ if task['log_archived_at'] is None:
757
+ raise TaskOpen(short_id)
758
+ # The harness chain row of a root task cascades, so an open
759
+ # task in its chain must not lose it. A legacy root has no
760
+ # stored chain_id.
761
+ chain_id = task['chain_id'] or task['id']
762
+ cursor = await conn.execute(
763
+ 'SELECT 1 FROM agent_runtime.tasks'
764
+ " WHERE status <> 'closed' AND (id = %s OR chain_id = %s"
765
+ ' OR (chain_id IS NULL AND id = %s)'
766
+ " OR (origin_kind = 'task' AND origin_id = %s)) LIMIT 1",
767
+ (task['id'], chain_id, chain_id, str(task['id'])),
768
+ )
769
+ if await cursor.fetchone() is not None:
770
+ raise TaskOpen(short_id)
771
+ # The lease sweep still needs the chain row of a root that
772
+ # waits for its fencers, and Broker.sweep joins the open
773
+ # resolutions of a failed close hook to the task.
774
+ cursor = await conn.execute(
775
+ 'SELECT 1 FROM harness.chains WHERE root_task_id = %s'
776
+ " AND state IN ('running', 'fencing', 'stuck')"
777
+ ' UNION ALL SELECT 1 FROM secrets.resolutions'
778
+ " WHERE task_id = %s AND state IN ('pending', 'active')"
779
+ ' LIMIT 1',
780
+ (task['id'], task['id']),
781
+ )
782
+ if await cursor.fetchone() is not None:
783
+ raise TaskOpen(short_id)
784
+ await conn.execute(
785
+ 'DELETE FROM agent_runtime.task_dependencies'
786
+ ' WHERE organization_id = %s'
787
+ ' AND (dependent_task_id = %s OR prerequisite_task_id = %s)',
788
+ (organization_id, task['id'], task['id']),
789
+ )
790
+ await conn.execute(
791
+ 'DELETE FROM agent_runtime.task_projects'
792
+ ' WHERE organization_id = %s AND task_id = %s',
793
+ (organization_id, task['id']),
794
+ )
795
+ await conn.execute(
796
+ 'DELETE FROM agent_runtime.tasks WHERE id = %s', (task['id'],)
797
+ )
798
+ return task
799
+
712
800
  async def set_control(
713
801
  self,
714
802
  organization_id: str,
@@ -244,6 +244,7 @@ REFUSED_ROUTES: frozenset[str] = frozenset(
244
244
  'PUT /prompts/{namespace}/{slug}/labels/{label}',
245
245
  'PUT /prompts/{namespace}/{slug}/versions/{n}/evaluation',
246
246
  # Agent tasks: an agent works on its tasks through the harness routes only, so it cannot answer its own approval or question, reassign, or start another agent outside its chain
247
+ 'DELETE /organizations/{org_slug}/agent-tasks/{short_id}',
247
248
  'DELETE /organizations/{org_slug}/agent-tasks/{short_id}/projects/{project_id}',
248
249
  'DELETE /organizations/{org_slug}/agent-tasks/{short_id}/requires/{prerequisite}',
249
250
  'POST /organizations/{org_slug}/agent-tasks/',
@@ -484,6 +484,13 @@ STANDARD_PERMISSIONS: list[tuple[str, str, str, str]] = [
484
484
  'resolve',
485
485
  'Answer feedback requests and resolve approvals of agent tasks',
486
486
  ),
487
+ # Granted to no non-admin default role.
488
+ (
489
+ 'agent_task:delete',
490
+ 'agent_task',
491
+ 'delete',
492
+ 'Delete closed agent tasks',
493
+ ),
487
494
  # Workflows (ADR 0021)
488
495
  (
489
496
  'workflow:read',
@@ -144,9 +144,12 @@ async def run_sync(
144
144
  :class:`CommitSyncUnavailable` when no integration provides the
145
145
  capability; other failures propagate so the caller can record them.
146
146
 
147
- Once the tags are in, enriches any that carry no release notes -- a tag
148
- cut outside imbi has no ``Release`` node, so this is what gives it a
149
- title and body to show. Then re-reads the drift notes ref, because
147
+ Once the tags are in, points every tagged ``Release`` node at the
148
+ commit its tag now names -- a tag re-promoted after a failed build
149
+ keeps its first commit on the node, and no other sync rewrites it.
150
+ Then enriches any that carry no release notes -- a tag cut outside
151
+ imbi has no ``Release`` node, so this is what gives it a title and
152
+ body to show. Then re-reads the drift notes ref, because
150
153
  the commits this just recorded have no verdicts and readers fail
151
154
  closed on that: recovering a commit without its verdict shows it as
152
155
  drifted. Both are best-effort: a failure there is logged and leaves
@@ -164,6 +167,7 @@ async def run_sync(
164
167
  )
165
168
  handler = typing.cast('CommitSyncCapability', resolved.capability_cls())
166
169
  counts = await handler.sync_all_history(ctx=ctx, credentials=credentials)
170
+ await _reconcile_release_committishes(db, org_slug, project_id)
167
171
  await _backfill_release_notes(db, org_slug, project_id)
168
172
  await _resync_drift_verdicts(db, org_slug, project_id)
169
173
  return counts
@@ -203,6 +207,34 @@ async def run_tag_sync(
203
207
  return await handler.sync_tag(ctx=ctx, credentials=credentials, tag=tag)
204
208
 
205
209
 
210
+ async def _reconcile_release_committishes(
211
+ db: graph.Graph, org_slug: str, project_id: str
212
+ ) -> None:
213
+ """Heal Release nodes whose synced tag names another commit."""
214
+ # Imported here (not at module load) so the worker/service module
215
+ # never pulls the endpoints package at import time.
216
+ from imbi.api.endpoints import project_deployments
217
+
218
+ try:
219
+ healed = await project_deployments.reconcile_release_committishes(
220
+ db, org_slug=org_slug, project_id=project_id
221
+ )
222
+ except Exception: # noqa: BLE001
223
+ LOGGER.warning(
224
+ 'release committish reconcile failed for project %s',
225
+ project_id,
226
+ exc_info=True,
227
+ )
228
+ return
229
+ if healed:
230
+ LOGGER.info(
231
+ 'commit-sync healed the committish of %d release(s) for '
232
+ 'project %s',
233
+ healed,
234
+ project_id,
235
+ )
236
+
237
+
206
238
  async def _backfill_release_notes(
207
239
  db: graph.Graph, org_slug: str, project_id: str
208
240
  ) -> None:
@@ -1123,6 +1123,49 @@ async def cancel_agent_task(
1123
1123
  return await _set_control(store, org_id, short_id, 'cancel', auth)
1124
1124
 
1125
1125
 
1126
+ @agent_tasks_router.delete('/{short_id}', status_code=204)
1127
+ async def delete_agent_task(
1128
+ org_id: OrgId,
1129
+ short_id: str,
1130
+ store: agent_tasks.Store,
1131
+ auth: typing.Annotated[
1132
+ permissions.AuthContext,
1133
+ fastapi.Depends(permissions.require_permission('agent_task:delete')),
1134
+ ],
1135
+ ) -> None:
1136
+ """Delete a closed task, with its events and relations.
1137
+
1138
+ The secret audit and the usage records of the task stay.
1139
+
1140
+ Raises:
1141
+ 403: The caller has no ``agent_task:delete``, or is not a member
1142
+ of the org.
1143
+ 404: No such task.
1144
+ 409: The task, or a task in its chain, is not closed.
1145
+
1146
+ """
1147
+ try:
1148
+ task = await store.delete(org_id, short_id.upper())
1149
+ except agent_tasks.TaskNotFound as e:
1150
+ raise fastapi.HTTPException(
1151
+ status_code=404, detail=f'Task {short_id!r} not found'
1152
+ ) from e
1153
+ except agent_tasks.TaskOpen as e:
1154
+ raise fastapi.HTTPException(
1155
+ status_code=409,
1156
+ detail=(
1157
+ f'Task {short_id!r} or a task in its chain is not closed,'
1158
+ ' or its log is not archived yet'
1159
+ ),
1160
+ ) from e
1161
+ LOGGER.info(
1162
+ 'Agent task %s (%s) deleted by %s',
1163
+ task['short_id'],
1164
+ task['id'],
1165
+ _actor(auth).id,
1166
+ )
1167
+
1168
+
1126
1169
  @agent_tasks_router.post(
1127
1170
  '/{short_id}/reassign', response_model=AgentTaskResponse
1128
1171
  )
@@ -16,7 +16,8 @@ version, so the system prompt and the model come back too.
16
16
  Each agent is its own service principal (ADR 0020): a
17
17
  ``ServiceAccount`` that is ``MEMBER_OF`` the organization, with an
18
18
  ``ACTS_AS`` edge from the agent. It is made with the agent and deleted
19
- with it. It has no role, so it has no permissions.
19
+ with it. Its slug is the slugified agent name, and its membership has
20
+ the ``developer`` role.
20
21
 
21
22
  Every route needs the caller to be a member of the organization
22
23
  (``MEMBER_OF``), because the ``agent:*`` permissions are not scoped to
@@ -32,6 +33,7 @@ import fastapi
32
33
  import nanoid
33
34
  import psycopg.errors
34
35
  import pydantic
36
+ import slugify
35
37
 
36
38
  from imbi.api import agent_tools, harness, workflows
37
39
  from imbi.api.auth import organizations, permissions
@@ -421,13 +423,22 @@ CREATE (s:ServiceAccount {{id: {sa_id}, slug: {sa_slug},
421
423
  display_name: {sa_display_name},
422
424
  description: {sa_description},
423
425
  is_active: true, created_at: {sa_created_at}}})
424
- CREATE (s)-[:MEMBER_OF]->(o)
426
+ CREATE (s)-[:MEMBER_OF {{role: 'developer'}}]->(o)
425
427
  CREATE (a)-[:ACTS_AS]->(s)
426
428
  """
427
429
 
430
+ _SA_SLUG_TAKEN_QUERY: typing.LiteralString = """
431
+ MATCH (s:ServiceAccount {{slug: {slug}}})
432
+ RETURN s.id AS id
433
+ """
434
+
435
+ # A rename during concurrent lazy creation can give an agent two
436
+ # accounts. The order makes all later calls use the same one.
428
437
  _SERVICE_ACCOUNT_QUERY: typing.LiteralString = """
429
438
  MATCH (:Agent {{id: {id}}})-[:ACTS_AS]->(s:ServiceAccount)
430
439
  RETURN s.id AS id
440
+ ORDER BY s.created_at, s.id
441
+ LIMIT 1
431
442
  """
432
443
 
433
444
  _ENSURE_SERVICE_ACCOUNT_QUERY: typing.LiteralString = (
@@ -456,16 +467,32 @@ def _props(raw: typing.Any) -> dict[str, typing.Any] | None:
456
467
  return typing.cast('dict[str, typing.Any]', parsed)
457
468
 
458
469
 
459
- def _service_account_params(agent_id: str, name: str) -> dict[str, typing.Any]:
470
+ async def _service_account_params(
471
+ db: graph.Graph, agent_id: str, name: str
472
+ ) -> dict[str, typing.Any]:
460
473
  """Return the parameters of :data:`_CREATE_SERVICE_ACCOUNT`.
461
474
 
462
- The slug comes from the agent id, which does not change, so two
463
- requests that make the same service account collide on the unique
464
- slug index.
475
+ The slug is the slugified agent name. Service account slugs are
476
+ unique across all organizations, so when another service account
477
+ has that slug, the agent id is added to it. The agent id is also
478
+ added when the name gives an empty slug, or a slug in the ``imbi-``
479
+ namespace of the internal service accounts. Seeding adopts an
480
+ existing account with an internal slug, so an agent must not have
481
+ one: a slug that still starts with ``imbi-`` gets an ``agent-``
482
+ prefix.
465
483
  """
484
+ slug = slugify.slugify(name)
485
+ if (
486
+ not slug
487
+ or slug.startswith('imbi-')
488
+ or await db.execute(_SA_SLUG_TAKEN_QUERY, {'slug': slug}, ['id'])
489
+ ):
490
+ slug = slugify.slugify(f'{name} {agent_id}')
491
+ if slug.startswith('imbi-'):
492
+ slug = slugify.slugify(f'agent {slug}')
466
493
  return {
467
494
  'sa_id': nanoid.generate(),
468
- 'sa_slug': f'agent-{agent_id}',
495
+ 'sa_slug': slug,
469
496
  'sa_display_name': name,
470
497
  'sa_description': f'Service principal of the agent {name!r}',
471
498
  'sa_created_at': _now(),
@@ -483,18 +510,35 @@ async def ensure_service_account(
483
510
  records = await db.execute(
484
511
  _SERVICE_ACCOUNT_QUERY, {'id': agent_id}, ['id']
485
512
  )
486
- if not records:
513
+ attempts = 3
514
+ while not records and attempts:
515
+ attempts -= 1
516
+ params = await _service_account_params(db, agent_id, name)
517
+ # Read again after the slug check. When a concurrent request
518
+ # made the account of this agent first, its slug looks taken,
519
+ # and this read finds that account. Thus the agent does not
520
+ # get a second account.
521
+ records = await db.execute(
522
+ _SERVICE_ACCOUNT_QUERY, {'id': agent_id}, ['id']
523
+ )
524
+ if records:
525
+ break
487
526
  try:
527
+ # Empty when the agent does not exist.
488
528
  records = await db.execute(
489
529
  _ENSURE_SERVICE_ACCOUNT_QUERY,
490
- {'id': agent_id, **_service_account_params(agent_id, name)},
530
+ {'id': agent_id, **params},
491
531
  ['id'],
492
532
  )
493
- except psycopg.errors.UniqueViolation:
494
- # A concurrent request made it first.
495
- records = await db.execute(
496
- _SERVICE_ACCOUNT_QUERY, {'id': agent_id}, ['id']
497
- )
533
+ break
534
+ except psycopg.errors.UniqueViolation as exc:
535
+ if not attempts:
536
+ raise fastapi.HTTPException(
537
+ status_code=409,
538
+ detail=f'Service account slug for {name!r} is in use',
539
+ ) from exc
540
+ # A concurrent request took the slug first. Try again.
541
+ continue
498
542
  if not records:
499
543
  raise fastapi.HTTPException(
500
544
  status_code=404, detail=f'Agent {name!r} not found'
@@ -1160,17 +1204,38 @@ async def create_agent(
1160
1204
  + _CREATE_SERVICE_ACCOUNT
1161
1205
  + ' RETURN a.id AS id'
1162
1206
  )
1163
- with conflict_on_unique_violation(_slug_taken_detail(snapshot.slug)):
1164
- records = await db.execute(
1165
- query,
1166
- {
1167
- **props,
1168
- **_service_account_params(agent_id, snapshot.name),
1169
- 'org_slug': org_slug,
1170
- 'team_id': team_id,
1171
- },
1172
- ['id'],
1173
- )
1207
+ records: list[dict[str, typing.Any]] = []
1208
+ for attempt in range(2):
1209
+ try:
1210
+ records = await db.execute(
1211
+ query,
1212
+ {
1213
+ **props,
1214
+ **await _service_account_params(
1215
+ db, agent_id, snapshot.name
1216
+ ),
1217
+ 'org_slug': org_slug,
1218
+ 'team_id': team_id,
1219
+ },
1220
+ ['id'],
1221
+ )
1222
+ break
1223
+ except psycopg.errors.UniqueViolation as exc:
1224
+ # A concurrent create in another org can take the service
1225
+ # account slug first. The next attempt sees that slug as
1226
+ # taken and adds the agent id to it.
1227
+ if exc.diag.table_name != 'ServiceAccount':
1228
+ raise fastapi.HTTPException(
1229
+ status_code=409,
1230
+ detail=_slug_taken_detail(snapshot.slug),
1231
+ ) from exc
1232
+ if attempt:
1233
+ raise fastapi.HTTPException(
1234
+ status_code=409,
1235
+ detail=(
1236
+ f'Service account slug for {snapshot.name!r} is in use'
1237
+ ),
1238
+ ) from exc
1174
1239
  if not records:
1175
1240
  raise fastapi.HTTPException(
1176
1241
  status_code=404,
@@ -56,7 +56,6 @@ from imbi.api.secret_broker import store as secret_store
56
56
  from imbi.common import graph, models
57
57
  from imbi.common.prompts import rendering
58
58
  from imbi.common.prompts import resolve as prompt_resolve
59
- from imbi.plugins.github import _repos
60
59
 
61
60
  LOGGER = logging.getLogger(__name__)
62
61
 
@@ -688,7 +687,7 @@ async def _rendered_prompt(
688
687
  """Render the pinned prompt version; return it or the error.
689
688
 
690
689
  The template gets the variables that it declares among
691
- ``organization``, ``agent``, ``task``, and ``project`` (slugs and
690
+ ``organization``, ``agent``, ``task``, and ``project_slug`` (slugs and
692
691
  the short id).
693
692
  """
694
693
  if bundle.prompt_ref is None or bundle.prompt_version is None:
@@ -705,7 +704,7 @@ async def _rendered_prompt(
705
704
  'organization': org_slug,
706
705
  'agent': bundle.agent_slug or '',
707
706
  'task': task['short_id'],
708
- 'project': task.get('project_slug') or '',
707
+ 'project_slug': task.get('project_slug') or '',
709
708
  }
710
709
  rendered = await rendering.render(
711
710
  version,
@@ -746,23 +745,27 @@ async def _repositories(
746
745
  the GitHub integration verified, whatever this link says."""
747
746
  if not task.get('project_id'):
748
747
  return []
749
- host = 'github.com'
748
+ config: dict[str, typing.Any] = {}
750
749
  if settings.github_app_binding is not None:
751
750
  provider = await store.binding_provider(
752
751
  task['organization_id'], settings.github_app_binding
753
752
  )
754
753
  if provider is not None and provider['type'] == 'github_app':
755
- config: dict[str, typing.Any] = provider['config'] or {}
756
- host = str(config.get('host', host))
754
+ config = provider['config'] or {}
757
755
  url = (await _helpers.lookup_project_links(db, task['project_id'])).get(
758
756
  _REPOSITORY_LINK
759
757
  )
760
- found = None if url is None else _repos.parse_owner_repo(url, host)
761
- if url is None or found is None:
758
+ if url is None:
762
759
  return []
763
- return [
764
- BundleRepository(host=host, name=f'{found[0]}/{found[1]}', url=url)
765
- ]
760
+ try:
761
+ # The provider parses the link: an empty configuration is
762
+ # github.com.
763
+ found = providers.get('github_app').repository(config, url)
764
+ except providers.BrokerError:
765
+ return []
766
+ if found is None:
767
+ return []
768
+ return [BundleRepository(host=found[0], name=found[1], url=url)]
766
769
 
767
770
 
768
771
  async def _identity(
@@ -1806,35 +1809,40 @@ async def create_harness_child_task(
1806
1809
  'short_id': parent['short_id'],
1807
1810
  'agent_id': parent['agent_id'],
1808
1811
  }
1809
- task, created = await store.create(
1810
- agent_tasks.NewTask(
1811
- organization_id=parent['organization_id'],
1812
- agent_id=agent_id,
1813
- agent_version=agent_version,
1814
- prompt_version=prompt_version,
1815
- service_account_id=service_account_id,
1816
- project_id=parent['project_id'],
1817
- project_slug=parent['project_slug'],
1818
- title=data.title,
1819
- description=data.description,
1820
- origin_kind='task',
1821
- origin_id=origin_id,
1822
- origin=origin,
1823
- idempotency_key=data.idempotency_key,
1824
- owner=parent['owner'],
1825
- budget=budget,
1826
- chain_id=chain_id,
1827
- ),
1828
- agent_tasks.Actor('agent', parent['agent_id'], 'harness'),
1829
- {
1830
- 'title': data.title,
1831
- 'description': data.description,
1832
- 'origin': origin,
1833
- 'budget': None if budget is None else str(budget),
1834
- 'agent_version': agent_version,
1835
- 'prompt_version': prompt_version,
1836
- },
1837
- )
1812
+ try:
1813
+ task, created = await store.create(
1814
+ agent_tasks.NewTask(
1815
+ organization_id=parent['organization_id'],
1816
+ agent_id=agent_id,
1817
+ agent_version=agent_version,
1818
+ prompt_version=prompt_version,
1819
+ service_account_id=service_account_id,
1820
+ project_id=parent['project_id'],
1821
+ project_slug=parent['project_slug'],
1822
+ title=data.title,
1823
+ description=data.description,
1824
+ origin_kind='task',
1825
+ origin_id=origin_id,
1826
+ origin=origin,
1827
+ idempotency_key=data.idempotency_key,
1828
+ owner=parent['owner'],
1829
+ budget=budget,
1830
+ chain_id=chain_id,
1831
+ ),
1832
+ agent_tasks.Actor('agent', parent['agent_id'], 'harness'),
1833
+ {
1834
+ 'title': data.title,
1835
+ 'description': data.description,
1836
+ 'origin': origin,
1837
+ 'budget': None if budget is None else str(budget),
1838
+ 'agent_version': agent_version,
1839
+ 'prompt_version': prompt_version,
1840
+ },
1841
+ )
1842
+ except agent_tasks.TaskClosed as e:
1843
+ raise _conflict(
1844
+ 'task_closed', f'Task {parent["short_id"]} is closed'
1845
+ ) from e
1838
1846
  if not created:
1839
1847
  response.status_code = 200
1840
1848
  return task