devops-bot-sdk 1.6.38__tar.gz → 1.6.40__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 (200) hide show
  1. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/PKG-INFO +1 -1
  2. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
  3. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/devops_bot_sdk.egg-info/SOURCES.txt +9 -0
  4. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/__init__.py +1 -1
  5. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/agentd.py +26 -2
  6. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/client.py +139 -19
  7. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/git_ops.py +36 -3
  8. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/ipc/handlers.py +426 -40
  9. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/local_exec.py +14 -0
  10. devops_bot_sdk-1.6.40/sdk/memory_guard.py +159 -0
  11. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/platform_compat.py +31 -0
  12. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/run_auto.py +228 -26
  13. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/updater.py +227 -67
  14. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_auto_agent_dispatch.py +10 -2
  15. devops_bot_sdk-1.6.40/tests/test_crash_recovery.py +160 -0
  16. devops_bot_sdk-1.6.40/tests/test_done_transition.py +200 -0
  17. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_firewall_open_on_update.py +40 -6
  18. devops_bot_sdk-1.6.40/tests/test_memory_guard.py +189 -0
  19. devops_bot_sdk-1.6.40/tests/test_pr_identity.py +271 -0
  20. devops_bot_sdk-1.6.40/tests/test_project_path_resolution.py +260 -0
  21. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_run_auto_agent_only.py +20 -1
  22. devops_bot_sdk-1.6.40/tests/test_run_verdict_is_honest.py +147 -0
  23. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_task_file_and_plan_run.py +6 -2
  24. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_ticket_speed_fixes.py +8 -6
  25. devops_bot_sdk-1.6.40/tests/test_updater_reports_the_truth.py +328 -0
  26. devops_bot_sdk-1.6.40/tests/test_windows_loop_identity.py +193 -0
  27. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/README.md +0 -0
  28. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
  29. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
  30. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/devops_bot_sdk.egg-info/requires.txt +0 -0
  31. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/devops_bot_sdk.egg-info/top_level.txt +0 -0
  32. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/pyproject.toml +0 -0
  33. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/agents/__init__.py +0 -0
  34. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/agents/activity.py +0 -0
  35. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/agents/builder.py +0 -0
  36. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/agents/chat.py +0 -0
  37. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/agents/connectors.py +0 -0
  38. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/agents/engines/__init__.py +0 -0
  39. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/agents/engines/claude_code.py +0 -0
  40. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/agents/engines/http.py +0 -0
  41. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/agents/engines/mcp.py +0 -0
  42. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/agents/engines/n8n.py +0 -0
  43. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/agents/engines/shell.py +0 -0
  44. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/agents/integrations.py +0 -0
  45. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/agents/inventory.py +0 -0
  46. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/agents/library/__init__.py +0 -0
  47. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/agents/manifest.py +0 -0
  48. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/agents/prompt_studio.py +0 -0
  49. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/agents/prompt_template.py +0 -0
  50. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/agents/prompt_vars.py +0 -0
  51. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/agents/run_log.py +0 -0
  52. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/agents/schedules.py +0 -0
  53. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/agents/secrets.py +0 -0
  54. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/agents/ticket_run.py +0 -0
  55. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/agents/usage.py +0 -0
  56. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/agents/varstore.py +0 -0
  57. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/browser_profile.py +0 -0
  58. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/cli.py +0 -0
  59. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/collectors/__init__.py +0 -0
  60. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/collectors/files.py +0 -0
  61. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/collectors/process.py +0 -0
  62. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/collectors/screenshot.py +0 -0
  63. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/config.py +0 -0
  64. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/crucial.py +0 -0
  65. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/departments.py +0 -0
  66. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/design_verify/__init__.py +0 -0
  67. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/design_verify/assets.py +0 -0
  68. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/design_verify/bootstrap.py +0 -0
  69. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/design_verify/browser.py +0 -0
  70. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/design_verify/compare.py +0 -0
  71. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/design_verify/loop.py +0 -0
  72. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/estimator.py +0 -0
  73. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/exceptions.py +0 -0
  74. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/firewall.py +0 -0
  75. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/formats.py +0 -0
  76. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/github_fetch.py +0 -0
  77. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/github_ingest.py +0 -0
  78. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/graph_recon.py +0 -0
  79. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/graphify.py +0 -0
  80. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/hooks/__init__.py +0 -0
  81. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/hooks/crucial_guard.py +0 -0
  82. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/intake.py +0 -0
  83. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/ipc/__init__.py +0 -0
  84. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/ipc/electron_bridge.py +0 -0
  85. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/journal.py +0 -0
  86. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/models/__init__.py +0 -0
  87. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/models/envelope.py +0 -0
  88. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/models/requests.py +0 -0
  89. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/models/responses.py +0 -0
  90. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/models/snapshots.py +0 -0
  91. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/phase_timer.py +0 -0
  92. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/plan_run.py +0 -0
  93. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/prd_progress.py +0 -0
  94. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/proof_readers.py +0 -0
  95. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/providers.py +0 -0
  96. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/py.typed +0 -0
  97. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/resume.py +0 -0
  98. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/router.py +0 -0
  99. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/scheduler.py +0 -0
  100. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/self_report.py +0 -0
  101. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/self_report_local.py +0 -0
  102. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/sse.py +0 -0
  103. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/swagger.py +0 -0
  104. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/task_file.py +0 -0
  105. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/task_types.py +0 -0
  106. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/test.py +0 -0
  107. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/test_pipeline.py +0 -0
  108. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/ticket_fields.py +0 -0
  109. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/tiers/__init__.py +0 -0
  110. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/tiers/mini_agent.py +0 -0
  111. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/ui/__init__.py +0 -0
  112. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/ui/api.py +0 -0
  113. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/ui/dashboard.py +0 -0
  114. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/ui/health.py +0 -0
  115. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/ui/ip_allowlist.py +0 -0
  116. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/ui/jobs.py +0 -0
  117. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/ui/models.py +0 -0
  118. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/ui/runner.py +0 -0
  119. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/ui/security.py +0 -0
  120. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/ui/server.py +0 -0
  121. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/ui/service.py +0 -0
  122. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/ui/session.py +0 -0
  123. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/ui/setup_manager.py +0 -0
  124. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/ui/static/agents.html +0 -0
  125. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/ui/static/app.js +0 -0
  126. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/ui/static/chat.html +0 -0
  127. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/ui/static/custom-agents.html +0 -0
  128. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/ui/static/default-agents.html +0 -0
  129. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/ui/static/index.html +0 -0
  130. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/ui/static/report.html +0 -0
  131. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/ui/static/runs.html +0 -0
  132. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/ui/static/schedules.html +0 -0
  133. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/ui/static/setup.html +0 -0
  134. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/ui/static/styles.css +0 -0
  135. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/ui/static/tickets.html +0 -0
  136. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/ui/swagger.py +0 -0
  137. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/url_access.py +0 -0
  138. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/vps_health.py +0 -0
  139. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/sdk/write_hook.py +0 -0
  140. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/setup.cfg +0 -0
  141. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_agent_activity.py +0 -0
  142. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_agent_activity_tracking.py +0 -0
  143. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_agent_chat_features.py +0 -0
  144. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_agent_listing.py +0 -0
  145. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_agent_prompt_async.py +0 -0
  146. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_agent_prompt_models.py +0 -0
  147. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_agent_run_ticket.py +0 -0
  148. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_agent_schedules.py +0 -0
  149. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_agent_workspace_settings.py +0 -0
  150. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_agents_connectors_builder.py +0 -0
  151. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_allowed_tools_config.py +0 -0
  152. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_browser_profile.py +0 -0
  153. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_cheap_tier.py +0 -0
  154. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_client_jira_projects.py +0 -0
  155. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_config_token_diagnostics.py +0 -0
  156. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_configure_token_owner.py +0 -0
  157. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_console_encoding.py +0 -0
  158. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_departments.py +0 -0
  159. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_epic_decompose_run.py +0 -0
  160. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_epic_inherited_agent_id.py +0 -0
  161. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_epic_session_isolation.py +0 -0
  162. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_estimator.py +0 -0
  163. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_formats.py +0 -0
  164. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_git_action.py +0 -0
  165. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_github_fetch.py +0 -0
  166. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_github_ingest.py +0 -0
  167. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_graph_recon.py +0 -0
  168. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_graphify_build.py +0 -0
  169. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_graphify_hook_install.py +0 -0
  170. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_health.py +0 -0
  171. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_intake_job_outcome.py +0 -0
  172. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_intake_plan_review.py +0 -0
  173. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_journal.py +0 -0
  174. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_local_exec_provider.py +0 -0
  175. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_phase_timer.py +0 -0
  176. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_plan_new_command.py +0 -0
  177. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_plan_run_activity.py +0 -0
  178. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_prd_artifact.py +0 -0
  179. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_proof_readers.py +0 -0
  180. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_providers.py +0 -0
  181. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_recon_session_isolation.py +0 -0
  182. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_resume.py +0 -0
  183. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_router.py +0 -0
  184. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_run_outputs.py +0 -0
  185. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_schedule_api.py +0 -0
  186. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_scheduler.py +0 -0
  187. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_self_report.py +0 -0
  188. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_service_token_warning.py +0 -0
  189. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_swagger_command.py +0 -0
  190. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_task_file_format_doc.py +0 -0
  191. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_ticket_fields.py +0 -0
  192. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_ticket_run_resolve_by_id.py +0 -0
  193. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_tier_flag_parity.py +0 -0
  194. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_ui_gate.py +0 -0
  195. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_ui_lifespan.py +0 -0
  196. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_update_timer.py +0 -0
  197. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_updater_extras.py +0 -0
  198. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_url_access.py +0 -0
  199. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_vps_health.py +0 -0
  200. {devops_bot_sdk-1.6.38 → devops_bot_sdk-1.6.40}/tests/test_write_hook.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devops-bot-sdk
3
- Version: 1.6.38
3
+ Version: 1.6.40
4
4
  Summary: DevOps Bot Desktop SDK — thin client for the AgentOS Electron desktop app
5
5
  Author: noumanaziz2128
6
6
  License-Expression: LicenseRef-Proprietary
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devops-bot-sdk
3
- Version: 1.6.38
3
+ Version: 1.6.40
4
4
  Summary: DevOps Bot Desktop SDK — thin client for the AgentOS Electron desktop app
5
5
  Author: noumanaziz2128
6
6
  License-Expression: LicenseRef-Proprietary
@@ -26,6 +26,7 @@ sdk/graphify.py
26
26
  sdk/intake.py
27
27
  sdk/journal.py
28
28
  sdk/local_exec.py
29
+ sdk/memory_guard.py
29
30
  sdk/phase_timer.py
30
31
  sdk/plan_run.py
31
32
  sdk/platform_compat.py
@@ -140,7 +141,9 @@ tests/test_client_jira_projects.py
140
141
  tests/test_config_token_diagnostics.py
141
142
  tests/test_configure_token_owner.py
142
143
  tests/test_console_encoding.py
144
+ tests/test_crash_recovery.py
143
145
  tests/test_departments.py
146
+ tests/test_done_transition.py
144
147
  tests/test_epic_decompose_run.py
145
148
  tests/test_epic_inherited_agent_id.py
146
149
  tests/test_epic_session_isolation.py
@@ -158,10 +161,13 @@ tests/test_intake_job_outcome.py
158
161
  tests/test_intake_plan_review.py
159
162
  tests/test_journal.py
160
163
  tests/test_local_exec_provider.py
164
+ tests/test_memory_guard.py
161
165
  tests/test_phase_timer.py
162
166
  tests/test_plan_new_command.py
163
167
  tests/test_plan_run_activity.py
168
+ tests/test_pr_identity.py
164
169
  tests/test_prd_artifact.py
170
+ tests/test_project_path_resolution.py
165
171
  tests/test_proof_readers.py
166
172
  tests/test_providers.py
167
173
  tests/test_recon_session_isolation.py
@@ -169,6 +175,7 @@ tests/test_resume.py
169
175
  tests/test_router.py
170
176
  tests/test_run_auto_agent_only.py
171
177
  tests/test_run_outputs.py
178
+ tests/test_run_verdict_is_honest.py
172
179
  tests/test_schedule_api.py
173
180
  tests/test_scheduler.py
174
181
  tests/test_self_report.py
@@ -184,6 +191,8 @@ tests/test_ui_gate.py
184
191
  tests/test_ui_lifespan.py
185
192
  tests/test_update_timer.py
186
193
  tests/test_updater_extras.py
194
+ tests/test_updater_reports_the_truth.py
187
195
  tests/test_url_access.py
188
196
  tests/test_vps_health.py
197
+ tests/test_windows_loop_identity.py
189
198
  tests/test_write_hook.py
@@ -34,7 +34,7 @@ Rules:
34
34
  - All data egress through submit_webhook only
35
35
  """
36
36
 
37
- __version__ = "1.6.38" # SINGLE SOURCE OF TRUTH — bump on every change; pyproject,
37
+ __version__ = "1.6.40" # SINGLE SOURCE OF TRUTH — bump on every change; pyproject,
38
38
  __author__ = "AgentOS" # sdk.client.SDK_VERSION and the UI server version all read this.
39
39
 
40
40
  from sdk.client import BackendClient
@@ -438,7 +438,31 @@ async def _dispatch_agent_run(
438
438
  from sdk import write_hook
439
439
  from sdk.agents.engines.claude_code import ensure_agent_workspace
440
440
 
441
- _workspace = str(ensure_agent_workspace(manifest.name))
441
+ # Where the agent works. A TICKET-triggered run belongs in the repo the
442
+ # ticket names — this call site had the `task` all along and never passed
443
+ # it, so `claude_code.run` fell through to `ensure_agent_workspace()` and
444
+ # every agent-routed ticket wrote into `~/.agentos/runs/<agent>` on any
445
+ # drive and any OS, then reported success. A ticket that names nowhere,
446
+ # and every scheduled run, still gets the per-agent scratch directory:
447
+ # "summarise my PRs daily" has no repo and should not be given one.
448
+ _workspace = ""
449
+ if task:
450
+ try:
451
+ from sdk.ipc.handlers import resolve_project_path
452
+ _resolved = resolve_project_path(task, {})
453
+ if _resolved.source == "instructions":
454
+ _workspace = _resolved.path
455
+ elif _resolved.unresolved:
456
+ logger.warning(
457
+ "agentd.project_path_unresolved agent=%s task=%s named=%s",
458
+ manifest.name, task_id, _resolved.unresolved,
459
+ )
460
+ except Exception as exc: # noqa: BLE001 — never block a run on this
461
+ logger.warning("agentd.project_path_failed task=%s err=%s",
462
+ task_id, exc)
463
+ if not _workspace:
464
+ _workspace = str(ensure_agent_workspace(manifest.name))
465
+
442
466
  _record_write = write_hook.make_recorder(
443
467
  task_id=task_id or f"agent-{manifest.name}",
444
468
  repo_path=_workspace,
@@ -452,7 +476,7 @@ async def _dispatch_agent_run(
452
476
  _started = time.monotonic()
453
477
  local_result = await claude_code_engine.run(
454
478
  manifest, skill_text, mcp_config, extra_env=effective_secrets or None,
455
- on_event=_tee,
479
+ on_event=_tee, project_path=_workspace,
456
480
  )
457
481
  run_log.finish_run(_log_path, local_result)
458
482
  # Backstop for writes the stream did not surface (a resumed turn,
@@ -926,12 +926,18 @@ class BackendClient:
926
926
  except Exception:
927
927
  return "pending"
928
928
 
929
- async def github_token_record(self) -> dict | None:
930
- """GET /ml-api/github/token → the full record for the caller's GitHub link.
931
-
932
- ``{id, userId, githubId, username, accessToken, scope, createdAt, updatedAt}``
933
- or None when the user has no GitHub link, the token is rejected, or the
934
- backend is unreachable. Best-effort: never raises.
929
+ async def github_token_detail(self) -> tuple[dict | None, str]:
930
+ """``(record, reason)`` — the GitHub link record, and WHY when there is none.
931
+
932
+ The reason exists because collapsing every outcome to ``None`` made a whole
933
+ class of failure indistinguishable from "this user has no GitHub account".
934
+ A run then pushes with the box's SSH key instead of the person's token, and —
935
+ if ``gh`` is not installed — records ``pr_unavailable``, which the compliance
936
+ gate treats as an explicit non-issue. The ticket completes with no PR of
937
+ theirs, and nothing anywhere says why. Per-user by construction, which is how
938
+ one employee ends up with 20 tickets and one PR while everyone else is fine.
939
+
940
+ Best-effort: never raises.
935
941
  """
936
942
  from sdk.config import BE_DEFAULT_URL
937
943
 
@@ -945,30 +951,69 @@ class BackendClient:
945
951
  try:
946
952
  async with httpx.AsyncClient(timeout=15.0) as client:
947
953
  resp = await client.get(url, headers=headers)
948
- if resp.status_code >= 400:
949
- return None
950
- return (resp.json() or {}).get("data") or None
951
- except Exception:
952
- return None
954
+ except Exception as exc: # noqa: BLE001
955
+ return (None, f"the GitHub token service was unreachable ({type(exc).__name__})")
953
956
 
954
- async def github_token(self) -> str | None:
955
- """GET /ml-api/github/token → the user's GitHub access token (gho_…).
957
+ if resp.status_code in (401, 403):
958
+ return (None, (f"the GitHub token request was rejected "
959
+ f"({resp.status_code}) — the co_ token may be wrong or "
960
+ "the link revoked"))
961
+ if resp.status_code == 404:
962
+ return (None, "no GitHub account is linked to this user")
963
+ if resp.status_code >= 400:
964
+ return (None, f"the GitHub token service returned {resp.status_code}")
965
+ try:
966
+ record = (resp.json() or {}).get("data") or None
967
+ except Exception: # noqa: BLE001 — a proxy answering 2xx with HTML
968
+ return (None, "the GitHub token service returned an unreadable body")
969
+ if not record:
970
+ return (None, "no GitHub account is linked to this user")
971
+ if not (record.get("accessToken") or "").strip():
972
+ return (None, "the linked GitHub account has no access token stored")
973
+
974
+ # The scope is on the record and has never been looked at. A token without
975
+ # `repo` can often still clone, so the run gets all the way to the push or
976
+ # the PR call before failing — with the reason buried in a step line.
977
+ scope = str(record.get("scope") or "")
978
+ if scope and not any(s in scope for s in ("repo", "public_repo", "write")):
979
+ return (record, (f"the GitHub token's scope ('{scope}') may not allow "
980
+ "pushing or opening PRs"))
981
+ return (record, "")
956
982
 
957
- Used by the LOCAL agent to authenticate git/gh for clone/push/PR. The
958
- endpoint lives on the ML backend (devopsbot-be), authorised by the same
959
- co_ token. Best-effort: returns None on any failure so a run without a
960
- connected GitHub account still proceeds (clone/PR steps just won't auth).
983
+ async def github_token_record(self) -> dict | None:
984
+ """The GitHub link record, or None. See :meth:`github_token_detail` for why."""
985
+ record, _reason = await self.github_token_detail()
986
+ return record
987
+
988
+ async def github_token_with_reason(self) -> tuple[str | None, str]:
989
+ """``(token, reason)`` — the user's GitHub access token, and why when absent.
990
+
991
+ Only successes are cached, deliberately: a cached failure would make one bad
992
+ minute look like an unlinked account for the next half hour.
961
993
  """
962
994
  import time as _time
963
995
 
964
996
  now = _time.monotonic()
965
997
  cached = _GITHUB_TOKEN_CACHE.get(self._token)
966
998
  if cached and now < cached[1]:
967
- return cached[0]
968
- record = await self.github_token_record()
999
+ return (cached[0], "")
1000
+ record, reason = await self.github_token_detail()
969
1001
  token = (record or {}).get("accessToken") or None
970
1002
  if token:
971
1003
  _GITHUB_TOKEN_CACHE[self._token] = (token, now + _GITHUB_TOKEN_TTL_S)
1004
+ return (token, reason)
1005
+
1006
+ async def github_token(self) -> str | None:
1007
+ """GET /ml-api/github/token → the user's GitHub access token (gho_…).
1008
+
1009
+ Used by the LOCAL agent to authenticate git/gh for clone/push/PR. The
1010
+ endpoint lives on the ML backend (devopsbot-be), authorised by the same
1011
+ co_ token. Best-effort: returns None on any failure so a run without a
1012
+ connected GitHub account still proceeds (clone/PR steps just won't auth) —
1013
+ callers that report to a human should use :meth:`github_token_with_reason`,
1014
+ because "no token" and "the token call failed" need different fixes.
1015
+ """
1016
+ token, _reason = await self.github_token_with_reason()
972
1017
  return token
973
1018
 
974
1019
  async def ingest_github_prs(self, since: str | None = None) -> dict | None:
@@ -1153,6 +1198,81 @@ class BackendClient:
1153
1198
  logger.warning("client.mark_in_progress error task=%s err=%s", task_id, exc)
1154
1199
  return False
1155
1200
 
1201
+ async def get_task_status(self, task_id: str) -> str | None:
1202
+ """GET {BE}/tasks-ml/{id} → the ticket's CURRENT board status, or None.
1203
+
1204
+ ``None`` means "could not read it", which is deliberately NOT the same as
1205
+ "not Done". The Done safety net in ``handlers.handle_pipeline_approve``
1206
+ forces a status only when it can prove the board has not caught up;
1207
+ guessing on an unreadable ticket would be worse than leaving it alone.
1208
+
1209
+ This method is the one the safety net has always called — and it has never
1210
+ existed. Its absence raised ``AttributeError`` one line before
1211
+ ``set_task_status(task_id, "Done")``, outside that call's ``try``, on every
1212
+ ticket that had just been committed, pushed and PR'd; the auto loop's
1213
+ per-ticket handler then moved the finished ticket to Backlog. So every
1214
+ delivered ticket failed, and only the delivered ones.
1215
+
1216
+ The single-record endpoint is not guaranteed to be served — the backend's
1217
+ own reader documents the same uncertainty and carries the same fallback
1218
+ (``devops-bot-ai/modules/agents_tools/jira/agent.py``, ``get_task``) — so a
1219
+ non-200 or an unparseable body falls back to scanning the list endpoint for
1220
+ this id. Best-effort throughout: never raises.
1221
+ """
1222
+ from sdk.config import BE_DEFAULT_URL
1223
+
1224
+ if not task_id:
1225
+ return None
1226
+ headers = {**self._headers, "accept": "*/*", "developer-name": "null", "product": ""}
1227
+
1228
+ def _status_of(rec: object) -> str | None:
1229
+ if not isinstance(rec, dict):
1230
+ return None
1231
+ val = str(rec.get("status") or "").strip()
1232
+ return val or None
1233
+
1234
+ # 1. Direct single-record read. The body is `{data: {record: {...}}}` or
1235
+ # `{data: {...}}` depending on the endpoint — accept both.
1236
+ url = f"{BE_DEFAULT_URL.rstrip('/')}/tasks-ml/{task_id}"
1237
+ try:
1238
+ async with httpx.AsyncClient(timeout=20.0) as client:
1239
+ resp = await client.get(url, headers=headers)
1240
+ if resp.status_code < 400:
1241
+ data = (resp.json() or {}).get("data") or {}
1242
+ found = _status_of(data.get("record") if isinstance(data, dict) else None)
1243
+ if found is None and isinstance(data, dict) and data.get("id"):
1244
+ found = _status_of(data)
1245
+ if found:
1246
+ return found
1247
+ except Exception as exc: # noqa: BLE001 — fall through to the list scan
1248
+ logger.warning("client.get_task_status direct failed task=%s err=%s", task_id, exc)
1249
+
1250
+ # 2. Fallback: find the record in the list endpoint. Unfiltered, because
1251
+ # the point of the read is to learn which status the ticket is in.
1252
+ try:
1253
+ async with httpx.AsyncClient(timeout=30.0) as client:
1254
+ resp = await client.get(
1255
+ f"{BE_DEFAULT_URL.rstrip('/')}/tasks-ml",
1256
+ params={"limit": 10000}, headers=headers,
1257
+ )
1258
+ if resp.status_code >= 400:
1259
+ return None
1260
+ records = ((resp.json() or {}).get("data") or {}).get("records") or []
1261
+ want = str(task_id).strip().upper()
1262
+ for rec in records:
1263
+ if not isinstance(rec, dict):
1264
+ continue
1265
+ ident = {
1266
+ str(rec.get("id") or "").strip().upper(),
1267
+ str(rec.get("jiraIssueKey") or "").strip().upper(),
1268
+ str(rec.get("jiraKey") or "").strip().upper(),
1269
+ }
1270
+ if want in ident:
1271
+ return _status_of(rec)
1272
+ except Exception as exc: # noqa: BLE001
1273
+ logger.warning("client.get_task_status list failed task=%s err=%s", task_id, exc)
1274
+ return None
1275
+
1156
1276
  async def set_task_status(self, task_id: str, status: str) -> bool:
1157
1277
  """PUT {BE}/tasks-ml/{id} status=<status> (best-effort, arbitrary target).
1158
1278
 
@@ -283,8 +283,16 @@ async def _create_pr_via_api(
283
283
  )
284
284
  if resp.status_code == 201:
285
285
  return resp.json().get("html_url")
286
- # 422 = a PR already exists for this head (or validation issue). Try to
287
- # surface the existing open PR rather than reporting "no PR".
286
+ # 422 = a PR already exists for this head (or validation issue). Surfacing
287
+ # the existing open PR is right when the head really is THIS run's branch —
288
+ # a re-run pushing more commits to its own branch should report its own PR.
289
+ #
290
+ # It was catastrophic when the branch was shared: a constant branch name from
291
+ # boilerplate instructions or a placeholder-free template made every ticket
292
+ # push to one branch, and each got handed the first ticket's PR URL, passed
293
+ # compliance and raised nothing. Branch names are per-ticket now
294
+ # (`handlers._uniquify_branch`); this check is the second line of defence, so
295
+ # a PR is only ever claimed when its head ref is the branch we pushed.
288
296
  if resp.status_code == 422:
289
297
  async with httpx.AsyncClient(timeout=30.0) as c:
290
298
  r2 = await c.get(
@@ -292,7 +300,15 @@ async def _create_pr_via_api(
292
300
  params={"head": f"{owner}:{head}", "state": "open"},
293
301
  )
294
302
  if r2.status_code == 200 and r2.json():
295
- return r2.json()[0].get("html_url")
303
+ existing = r2.json()[0]
304
+ found_head = ((existing.get("head") or {}).get("ref") or "").strip()
305
+ if found_head and found_head != head:
306
+ logger.warning(
307
+ "git_ops.pr_head_mismatch asked=%s got=%s — not claiming it",
308
+ head, found_head,
309
+ )
310
+ else:
311
+ return existing.get("html_url")
296
312
  logger.warning(
297
313
  "git_ops.pr_api_failed owner=%s repo=%s head=%s status=%s body=%s",
298
314
  owner, repo, head, resp.status_code, resp.text[:300],
@@ -302,6 +318,23 @@ async def _create_pr_via_api(
302
318
  return None
303
319
 
304
320
 
321
+ async def has_local_changes(path: str) -> bool:
322
+ """True when the working tree has staged, unstaged or untracked changes.
323
+
324
+ The tool-level counters miss an entire class of real work: `_write_count` only
325
+ counts Write/Edit/MultiEdit/NotebookEdit, and `files_changed` only events that
326
+ carry a tool-result filePath. An agent that edits through **Bash** — `sed`,
327
+ `patch`, `git apply`, a scaffolding CLI — scores zero on both, so no commit,
328
+ push or PR was ever attempted and the ticket was then failed for "produced 0
329
+ committed changes". Git is the authority on whether the tree moved.
330
+ """
331
+ env = _git_env(None)
332
+ rc, out, _err = await _run(["git", "status", "--porcelain"], path, env, timeout=60.0)
333
+ if rc != 0:
334
+ return False
335
+ return bool(out.strip())
336
+
337
+
305
338
  async def finish_pr(
306
339
  project_path: str, branch: str, base: str, title: str, body: str,
307
340
  github_token: str | None,