devops-bot-sdk 1.6.32__tar.gz → 1.6.34__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 (183) hide show
  1. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/PKG-INFO +1 -1
  2. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
  3. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/devops_bot_sdk.egg-info/SOURCES.txt +21 -1
  4. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/__init__.py +2 -2
  5. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agentd.py +21 -0
  6. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/client.py +178 -17
  7. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/config.py +96 -0
  8. devops_bot_sdk-1.6.34/sdk/estimator.py +246 -0
  9. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/formats.py +28 -2
  10. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/graph_recon.py +137 -0
  11. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/graphify.py +309 -21
  12. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/intake.py +154 -9
  13. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ipc/handlers.py +422 -15
  14. devops_bot_sdk-1.6.34/sdk/journal.py +388 -0
  15. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/local_exec.py +259 -50
  16. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/plan_run.py +26 -4
  17. devops_bot_sdk-1.6.34/sdk/prd_progress.py +288 -0
  18. devops_bot_sdk-1.6.34/sdk/providers.py +631 -0
  19. devops_bot_sdk-1.6.34/sdk/resume.py +383 -0
  20. devops_bot_sdk-1.6.34/sdk/router.py +420 -0
  21. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/self_report.py +33 -14
  22. devops_bot_sdk-1.6.34/sdk/task_types.py +199 -0
  23. devops_bot_sdk-1.6.34/sdk/tiers/__init__.py +17 -0
  24. devops_bot_sdk-1.6.34/sdk/tiers/mini_agent.py +336 -0
  25. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/api.py +84 -1
  26. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/models.py +15 -1
  27. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/static/app.js +87 -10
  28. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/static/chat.html +27 -8
  29. devops_bot_sdk-1.6.34/sdk/write_hook.py +242 -0
  30. devops_bot_sdk-1.6.34/tests/test_cheap_tier.py +428 -0
  31. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_client_jira_projects.py +45 -4
  32. devops_bot_sdk-1.6.34/tests/test_estimator.py +285 -0
  33. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_formats.py +56 -5
  34. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_graphify_build.py +134 -4
  35. devops_bot_sdk-1.6.34/tests/test_journal.py +288 -0
  36. devops_bot_sdk-1.6.34/tests/test_local_exec_provider.py +295 -0
  37. devops_bot_sdk-1.6.34/tests/test_prd_artifact.py +473 -0
  38. devops_bot_sdk-1.6.34/tests/test_providers.py +378 -0
  39. devops_bot_sdk-1.6.34/tests/test_resume.py +495 -0
  40. devops_bot_sdk-1.6.34/tests/test_router.py +447 -0
  41. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_self_report.py +23 -1
  42. devops_bot_sdk-1.6.34/tests/test_tier_flag_parity.py +364 -0
  43. devops_bot_sdk-1.6.34/tests/test_write_hook.py +289 -0
  44. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/README.md +0 -0
  45. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
  46. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
  47. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/devops_bot_sdk.egg-info/requires.txt +0 -0
  48. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/devops_bot_sdk.egg-info/top_level.txt +0 -0
  49. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/pyproject.toml +0 -0
  50. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/__init__.py +0 -0
  51. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/activity.py +0 -0
  52. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/builder.py +0 -0
  53. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/chat.py +0 -0
  54. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/connectors.py +0 -0
  55. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/engines/__init__.py +0 -0
  56. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/engines/claude_code.py +0 -0
  57. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/engines/http.py +0 -0
  58. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/engines/mcp.py +0 -0
  59. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/engines/n8n.py +0 -0
  60. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/engines/shell.py +0 -0
  61. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/integrations.py +0 -0
  62. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/inventory.py +0 -0
  63. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/library/__init__.py +0 -0
  64. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/manifest.py +0 -0
  65. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/prompt_studio.py +0 -0
  66. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/prompt_template.py +0 -0
  67. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/prompt_vars.py +0 -0
  68. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/run_log.py +0 -0
  69. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/schedules.py +0 -0
  70. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/secrets.py +0 -0
  71. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/ticket_run.py +0 -0
  72. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/usage.py +0 -0
  73. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/agents/varstore.py +0 -0
  74. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/browser_profile.py +0 -0
  75. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/cli.py +0 -0
  76. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/collectors/__init__.py +0 -0
  77. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/collectors/files.py +0 -0
  78. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/collectors/process.py +0 -0
  79. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/collectors/screenshot.py +0 -0
  80. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/crucial.py +0 -0
  81. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/departments.py +0 -0
  82. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/design_verify/__init__.py +0 -0
  83. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/design_verify/assets.py +0 -0
  84. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/design_verify/bootstrap.py +0 -0
  85. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/design_verify/browser.py +0 -0
  86. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/design_verify/compare.py +0 -0
  87. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/design_verify/loop.py +0 -0
  88. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/exceptions.py +0 -0
  89. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/git_ops.py +0 -0
  90. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/github_ingest.py +0 -0
  91. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/hooks/__init__.py +0 -0
  92. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/hooks/crucial_guard.py +0 -0
  93. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ipc/__init__.py +0 -0
  94. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ipc/electron_bridge.py +0 -0
  95. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/models/__init__.py +0 -0
  96. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/models/envelope.py +0 -0
  97. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/models/requests.py +0 -0
  98. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/models/responses.py +0 -0
  99. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/models/snapshots.py +0 -0
  100. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/phase_timer.py +0 -0
  101. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/platform_compat.py +0 -0
  102. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/proof_readers.py +0 -0
  103. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/py.typed +0 -0
  104. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/run_auto.py +0 -0
  105. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/scheduler.py +0 -0
  106. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/self_report_local.py +0 -0
  107. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/sse.py +0 -0
  108. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/task_file.py +0 -0
  109. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/test.py +0 -0
  110. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/test_pipeline.py +0 -0
  111. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ticket_fields.py +0 -0
  112. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/__init__.py +0 -0
  113. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/dashboard.py +0 -0
  114. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/health.py +0 -0
  115. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/ip_allowlist.py +0 -0
  116. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/jobs.py +0 -0
  117. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/runner.py +0 -0
  118. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/security.py +0 -0
  119. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/server.py +0 -0
  120. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/service.py +0 -0
  121. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/session.py +0 -0
  122. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/setup_manager.py +0 -0
  123. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/static/agents.html +0 -0
  124. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/static/custom-agents.html +0 -0
  125. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/static/default-agents.html +0 -0
  126. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/static/index.html +0 -0
  127. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/static/login.html +0 -0
  128. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/static/report.html +0 -0
  129. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/static/runs.html +0 -0
  130. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/static/schedules.html +0 -0
  131. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/static/setup.html +0 -0
  132. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/static/styles.css +0 -0
  133. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/ui/static/tickets.html +0 -0
  134. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/updater.py +0 -0
  135. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/url_access.py +0 -0
  136. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/sdk/vps_health.py +0 -0
  137. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/setup.cfg +0 -0
  138. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_agent_activity.py +0 -0
  139. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_agent_activity_tracking.py +0 -0
  140. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_agent_chat_features.py +0 -0
  141. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_agent_listing.py +0 -0
  142. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_agent_prompt_async.py +0 -0
  143. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_agent_prompt_models.py +0 -0
  144. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_agent_run_ticket.py +0 -0
  145. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_agent_schedules.py +0 -0
  146. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_agent_workspace_settings.py +0 -0
  147. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_agents_connectors_builder.py +0 -0
  148. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_allowed_tools_config.py +0 -0
  149. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_auto_agent_dispatch.py +0 -0
  150. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_browser_profile.py +0 -0
  151. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_config_token_diagnostics.py +0 -0
  152. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_configure_token_owner.py +0 -0
  153. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_console_encoding.py +0 -0
  154. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_departments.py +0 -0
  155. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_epic_decompose_run.py +0 -0
  156. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_epic_inherited_agent_id.py +0 -0
  157. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_epic_session_isolation.py +0 -0
  158. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_git_action.py +0 -0
  159. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_github_ingest.py +0 -0
  160. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_graph_recon.py +0 -0
  161. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_graphify_hook_install.py +0 -0
  162. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_health.py +0 -0
  163. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_intake_plan_review.py +0 -0
  164. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_otp_login.py +0 -0
  165. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_phase_timer.py +0 -0
  166. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_plan_new_command.py +0 -0
  167. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_plan_run_activity.py +0 -0
  168. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_proof_readers.py +0 -0
  169. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_recon_session_isolation.py +0 -0
  170. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_run_auto_agent_only.py +0 -0
  171. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_run_outputs.py +0 -0
  172. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_schedule_api.py +0 -0
  173. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_scheduler.py +0 -0
  174. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_service_token_warning.py +0 -0
  175. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_task_file_and_plan_run.py +0 -0
  176. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_task_file_format_doc.py +0 -0
  177. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_ticket_fields.py +0 -0
  178. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_ticket_run_resolve_by_id.py +0 -0
  179. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_ticket_speed_fixes.py +0 -0
  180. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_update_timer.py +0 -0
  181. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_updater_extras.py +0 -0
  182. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_url_access.py +0 -0
  183. {devops_bot_sdk-1.6.32 → devops_bot_sdk-1.6.34}/tests/test_vps_health.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devops-bot-sdk
3
- Version: 1.6.32
3
+ Version: 1.6.34
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.32
3
+ Version: 1.6.34
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
@@ -14,6 +14,7 @@ sdk/client.py
14
14
  sdk/config.py
15
15
  sdk/crucial.py
16
16
  sdk/departments.py
17
+ sdk/estimator.py
17
18
  sdk/exceptions.py
18
19
  sdk/formats.py
19
20
  sdk/git_ops.py
@@ -21,24 +22,31 @@ sdk/github_ingest.py
21
22
  sdk/graph_recon.py
22
23
  sdk/graphify.py
23
24
  sdk/intake.py
25
+ sdk/journal.py
24
26
  sdk/local_exec.py
25
27
  sdk/phase_timer.py
26
28
  sdk/plan_run.py
27
29
  sdk/platform_compat.py
30
+ sdk/prd_progress.py
28
31
  sdk/proof_readers.py
32
+ sdk/providers.py
29
33
  sdk/py.typed
34
+ sdk/resume.py
35
+ sdk/router.py
30
36
  sdk/run_auto.py
31
37
  sdk/scheduler.py
32
38
  sdk/self_report.py
33
39
  sdk/self_report_local.py
34
40
  sdk/sse.py
35
41
  sdk/task_file.py
42
+ sdk/task_types.py
36
43
  sdk/test.py
37
44
  sdk/test_pipeline.py
38
45
  sdk/ticket_fields.py
39
46
  sdk/updater.py
40
47
  sdk/url_access.py
41
48
  sdk/vps_health.py
49
+ sdk/write_hook.py
42
50
  sdk/agents/__init__.py
43
51
  sdk/agents/activity.py
44
52
  sdk/agents/builder.py
@@ -83,6 +91,8 @@ sdk/models/envelope.py
83
91
  sdk/models/requests.py
84
92
  sdk/models/responses.py
85
93
  sdk/models/snapshots.py
94
+ sdk/tiers/__init__.py
95
+ sdk/tiers/mini_agent.py
86
96
  sdk/ui/__init__.py
87
97
  sdk/ui/api.py
88
98
  sdk/ui/dashboard.py
@@ -122,6 +132,7 @@ tests/test_agents_connectors_builder.py
122
132
  tests/test_allowed_tools_config.py
123
133
  tests/test_auto_agent_dispatch.py
124
134
  tests/test_browser_profile.py
135
+ tests/test_cheap_tier.py
125
136
  tests/test_client_jira_projects.py
126
137
  tests/test_config_token_diagnostics.py
127
138
  tests/test_configure_token_owner.py
@@ -130,6 +141,7 @@ tests/test_departments.py
130
141
  tests/test_epic_decompose_run.py
131
142
  tests/test_epic_inherited_agent_id.py
132
143
  tests/test_epic_session_isolation.py
144
+ tests/test_estimator.py
133
145
  tests/test_formats.py
134
146
  tests/test_git_action.py
135
147
  tests/test_github_ingest.py
@@ -138,12 +150,18 @@ tests/test_graphify_build.py
138
150
  tests/test_graphify_hook_install.py
139
151
  tests/test_health.py
140
152
  tests/test_intake_plan_review.py
153
+ tests/test_journal.py
154
+ tests/test_local_exec_provider.py
141
155
  tests/test_otp_login.py
142
156
  tests/test_phase_timer.py
143
157
  tests/test_plan_new_command.py
144
158
  tests/test_plan_run_activity.py
159
+ tests/test_prd_artifact.py
145
160
  tests/test_proof_readers.py
161
+ tests/test_providers.py
146
162
  tests/test_recon_session_isolation.py
163
+ tests/test_resume.py
164
+ tests/test_router.py
147
165
  tests/test_run_auto_agent_only.py
148
166
  tests/test_run_outputs.py
149
167
  tests/test_schedule_api.py
@@ -155,7 +173,9 @@ tests/test_task_file_format_doc.py
155
173
  tests/test_ticket_fields.py
156
174
  tests/test_ticket_run_resolve_by_id.py
157
175
  tests/test_ticket_speed_fixes.py
176
+ tests/test_tier_flag_parity.py
158
177
  tests/test_update_timer.py
159
178
  tests/test_updater_extras.py
160
179
  tests/test_url_access.py
161
- tests/test_vps_health.py
180
+ tests/test_vps_health.py
181
+ tests/test_write_hook.py
@@ -1,6 +1,6 @@
1
1
  """AgentOS Desktop SDK — thin HTTPS/SSE client for the Electron app.
2
2
 
3
- Version: 1.6.32
3
+ Version: 1.6.34
4
4
 
5
5
  Public surface:
6
6
  BackendClient.from_config() — create client from ~/.agentos/config.toml
@@ -34,7 +34,7 @@ Rules:
34
34
  - All data egress through submit_webhook only
35
35
  """
36
36
 
37
- __version__ = "1.6.32" # SINGLE SOURCE OF TRUTH — bump on every change; pyproject,
37
+ __version__ = "1.6.34" # 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
@@ -429,8 +429,25 @@ async def _dispatch_agent_run(
429
429
  )
430
430
  run_log.log_line(_log_path, f"task_id={task_id}")
431
431
 
432
+ # A custom-agent run writes real files, so it gets the SAME shared
433
+ # post-write hook the coding path uses: journal (synchronous,
434
+ # survives a kill) → requirement overlay → graph refresh. Without
435
+ # this, a custom-agent run produced no graph update and no journal
436
+ # entry at all, so a tier switch during one had no anchor to resume
437
+ # from — the run type was simply outside the resume triple.
438
+ from sdk import write_hook
439
+ from sdk.agents.engines.claude_code import ensure_agent_workspace
440
+
441
+ _workspace = str(ensure_agent_workspace(manifest.name))
442
+ _record_write = write_hook.make_recorder(
443
+ task_id=task_id or f"agent-{manifest.name}",
444
+ repo_path=_workspace,
445
+ )
446
+ _write_events = write_hook.make_event_hook(_record_write)
447
+
432
448
  async def _tee(ev: dict) -> None:
433
449
  run_log.log_event(_log_path, ev)
450
+ await _write_events(ev)
434
451
 
435
452
  _started = time.monotonic()
436
453
  local_result = await claude_code_engine.run(
@@ -438,6 +455,10 @@ async def _dispatch_agent_run(
438
455
  on_event=_tee,
439
456
  )
440
457
  run_log.finish_run(_log_path, local_result)
458
+ # Backstop for writes the stream did not surface (a resumed turn,
459
+ # an engine that tees events elsewhere). Ordering is lost, but an
460
+ # ordered-but-empty journal is worse than an unordered complete one.
461
+ await write_hook.record_result_files(_record_write, local_result)
441
462
  # Local ledger + platform `agent_activities` feed. EVERY trigger path
442
463
  # reaches this function (cron schedule, ticket label, on-demand
443
464
  # run-on-ticket from the UI / CLI / desktop IPC), so a VPS agent run is
@@ -197,6 +197,30 @@ _ORCHESTRATE_TIMEOUT = 2700.0 # 45 min — covers approval wait + VPS exec
197
197
  _AUTO_ORCHESTRATE_TIMEOUT = 2700.0 # 45 min — same budget for multi-task auto runs
198
198
 
199
199
 
200
+ def _jira_project_records(payload: dict) -> list[dict]:
201
+ """Normalise a jira-projects envelope to `list[dict]`.
202
+
203
+ The endpoint returns `data` as a FLAT LIST. Sibling endpoints on the same
204
+ gateway wrap theirs in `data.records` / `data.record`, and this shape is not
205
+ documented, so all three are tolerated rather than guessing one — a wrong
206
+ guess reads as "no projects", which is indistinguishable from a real empty
207
+ result and would silently disable the whole picker.
208
+ """
209
+ data = payload.get("data")
210
+ if isinstance(data, list):
211
+ return [r for r in data if isinstance(r, dict)]
212
+ if isinstance(data, dict):
213
+ records = data.get("records")
214
+ if isinstance(records, list):
215
+ return [r for r in records if isinstance(r, dict)]
216
+ record = data.get("record")
217
+ if isinstance(record, dict):
218
+ return [record]
219
+ if data.get("id") or data.get("jiraProjectId"):
220
+ return [dict(data)]
221
+ return []
222
+
223
+
200
224
  class BackendClient:
201
225
  """Thin HTTPS/SSE client — wraps all backend /api/v1/* endpoints.
202
226
 
@@ -1096,6 +1120,108 @@ class BackendClient:
1096
1120
  logger.warning("client.set_task_status error task=%s err=%s", task_id, exc)
1097
1121
  return False
1098
1122
 
1123
+ # ── Cheap tier (T0) — the backend holds the Azure/GCP credentials ───
1124
+
1125
+ async def llm_complete(
1126
+ self,
1127
+ messages: list[dict],
1128
+ *,
1129
+ tools: list[dict] | None = None,
1130
+ step_type: str = "small_task",
1131
+ task_id: str | None = None,
1132
+ budget_tokens: int | None = None,
1133
+ used_tokens: int = 0,
1134
+ max_tokens: int | None = None,
1135
+ timeout: float = 120.0,
1136
+ attempts: int = 3,
1137
+ ) -> dict:
1138
+ """POST /api/v1/llm/complete — one turn of the cheap-tier tool loop.
1139
+
1140
+ The credential asymmetry is deliberate (plan §6): Azure/GCP keys stay
1141
+ **server-side** because the backend is the client for T0, while the
1142
+ Bedrock/subscription credentials stay desktop-side because the local
1143
+ ``claude`` CLI is the client for T1/T2. Each tier's creds live where that
1144
+ tier's client runs, so the desktop never holds an Azure key.
1145
+
1146
+ The budget is enforced **server-side** — the desktop is untrusted for
1147
+ accounting — and a breach comes back as ``409 budget_exhausted``, which
1148
+ the router treats as "escalate to T1", not as an error.
1149
+
1150
+ Returns ``{"ok", "text", "tool_calls", "model", "tokens_in",
1151
+ "tokens_out", "status", "reason"}``. Never raises: an unreachable
1152
+ backend is a *routing signal* (the cheap tier is the LESS available one),
1153
+ so it comes back as ``ok=False`` with ``reason="backend_unreachable"``
1154
+ and the router escalates.
1155
+
1156
+ Retries only what is worth retrying, via the existing
1157
+ ``is_retryable_backend_error`` — so a saturated Postgres pool surfaced
1158
+ as a 5xx is retried rather than escalating the whole task off the cheap
1159
+ tier for a transient blip.
1160
+ """
1161
+ path = "/api/v1/llm/complete"
1162
+ payload: dict[str, Any] = {
1163
+ "messages": messages,
1164
+ "step_type": step_type,
1165
+ }
1166
+ if tools:
1167
+ payload["tools"] = tools
1168
+ if task_id:
1169
+ payload["task_id"] = task_id
1170
+ if budget_tokens is not None:
1171
+ payload["budget_tokens"] = budget_tokens
1172
+ if used_tokens:
1173
+ payload["used_tokens"] = used_tokens
1174
+ if max_tokens is not None:
1175
+ payload["max_tokens"] = max_tokens
1176
+
1177
+ def _fail(reason: str, status: int = 0, detail: str = "") -> dict:
1178
+ return {"ok": False, "text": "", "tool_calls": [], "model": None,
1179
+ "tokens_in": 0, "tokens_out": 0, "status": status,
1180
+ "reason": reason, "detail": detail[:300]}
1181
+
1182
+ last = _fail("backend_unreachable")
1183
+ for attempt in range(max(1, attempts)):
1184
+ try:
1185
+ async with httpx.AsyncClient(timeout=timeout) as client:
1186
+ resp = await client.post(
1187
+ self._url(path), json=payload, headers=self._headers,
1188
+ )
1189
+ except Exception as exc: # noqa: BLE001 — network/DNS/TLS
1190
+ last = _fail("backend_unreachable", detail=str(exc))
1191
+ if attempt + 1 < attempts:
1192
+ await asyncio.sleep(min(2.0 * (attempt + 1), 5.0))
1193
+ continue
1194
+ return last
1195
+ body = resp.text or ""
1196
+ if resp.status_code == 409:
1197
+ # The hard ceiling. One mechanism serves both accounting and the
1198
+ # cap, so there is nothing to reconcile between them.
1199
+ return _fail("budget_exhausted", resp.status_code, body)
1200
+ if resp.status_code >= 400:
1201
+ if is_retryable_backend_error(resp.status_code, body) and attempt + 1 < attempts:
1202
+ last = _fail("retryable", resp.status_code, body)
1203
+ await asyncio.sleep(min(2.0 * (attempt + 1), 5.0))
1204
+ continue
1205
+ reason = ("auth" if resp.status_code == 401
1206
+ else "unavailable" if resp.status_code >= 500
1207
+ else "rejected")
1208
+ return _fail(reason, resp.status_code, body)
1209
+ try:
1210
+ data = resp.json() or {}
1211
+ except Exception: # noqa: BLE001 — a 200 with a non-JSON body
1212
+ return _fail("bad_response", resp.status_code, body)
1213
+ return {
1214
+ "ok": True,
1215
+ "text": str(data.get("text") or ""),
1216
+ "tool_calls": list(data.get("tool_calls") or []),
1217
+ "model": data.get("model"),
1218
+ "tokens_in": int(data.get("tokens_in") or 0),
1219
+ "tokens_out": int(data.get("tokens_out") or 0),
1220
+ "status": resp.status_code,
1221
+ "reason": "",
1222
+ }
1223
+ return last
1224
+
1099
1225
  async def update_task_ml(
1100
1226
  self, task_id: str, fields: dict, model_used: list[str] | None = None,
1101
1227
  ) -> bool:
@@ -1214,15 +1340,32 @@ class BackendClient:
1214
1340
  return None
1215
1341
 
1216
1342
  async def list_jira_projects(
1217
- self, pv_id: str, limit: int = 100, offset: int = 0,
1343
+ self, pv_id: str = "", limit: int = 100, offset: int = 0,
1344
+ *, all_: bool = False,
1218
1345
  ) -> list[dict]:
1219
- """GET {BE}/ml-api/jira-projects?limit=&offset=&pvId= — project records.
1220
-
1221
- Scoped to one product version. Used to resolve an existing project (match
1222
- on `key`) before falling back to create_jira_project. Pagination is the
1223
- caller's: `total` in the envelope is ignored. Returns [] on any failure —
1224
- unlike list_todo_tasks_raw, a failure is logged rather than silent, so a
1225
- broken gateway can't read as "this version has no projects".
1346
+ """GET {BE}/ml-api/jira-projects — project records.
1347
+
1348
+ Two fixes over the original, both of which made an unfiltered listing
1349
+ impossible:
1350
+
1351
+ 1. `pvId` is now OMITTED when blank. It used to be sent unconditionally as
1352
+ `pvId=""`, which scopes the query to nothing, so there was no way to
1353
+ ask "every project I can see". That matters because `pvId` is null on
1354
+ the large majority of live rows (57 projects, 3 with a pvId at time of
1355
+ writing) — a pvId-only client can reach almost none of the boards a
1356
+ person would actually name.
1357
+ 2. The envelope is read TOLERANTLY. This endpoint returns `data` as a
1358
+ FLAT LIST; the original read `data.records` only, so an unfiltered call
1359
+ would have returned `[]` even once (1) was fixed. Both shapes are now
1360
+ accepted, plus a bare `data.record`.
1361
+
1362
+ Pass `all_=True` to send the gateway's `all=true` (no pagination) instead
1363
+ of `limit`/`offset`. Match on `key` to resolve an existing project before
1364
+ falling back to `create_jira_project` — `key` is the same string a ticket
1365
+ carries as `projectKey`.
1366
+
1367
+ Returns [] on any failure, logged rather than silent, so a broken gateway
1368
+ cannot read as "there are no projects".
1226
1369
  """
1227
1370
  from sdk.config import BE_DEFAULT_URL
1228
1371
 
@@ -1233,25 +1376,43 @@ class BackendClient:
1233
1376
  "developer-name": "null",
1234
1377
  "product": "",
1235
1378
  }
1379
+ params: dict[str, object] = {"offset": offset}
1380
+ if all_:
1381
+ params["all"] = "true"
1382
+ else:
1383
+ params["limit"] = limit
1384
+ # Only scope by product when there is one — see (1) above.
1385
+ if str(pv_id or "").strip():
1386
+ params["pvId"] = str(pv_id).strip()
1236
1387
  try:
1237
- # All three params are always sent — the wire URL keeps its documented
1238
- # shape even when the caller has no pvId to scope by.
1239
1388
  async with httpx.AsyncClient(timeout=30.0) as client:
1240
- resp = await client.get(
1241
- url,
1242
- params={"limit": limit, "offset": offset, "pvId": str(pv_id or "")},
1243
- headers=headers,
1244
- )
1389
+ resp = await client.get(url, params=params, headers=headers)
1245
1390
  if resp.status_code >= 400:
1246
1391
  logger.warning("client.list_jira_projects failed status=%s body=%s",
1247
1392
  resp.status_code, resp.text[:300])
1248
1393
  return []
1249
- records = ((resp.json() or {}).get("data") or {}).get("records") or []
1250
- return records if isinstance(records, list) else []
1394
+ return _jira_project_records(resp.json() or {})
1251
1395
  except Exception as exc: # noqa: BLE001
1252
1396
  logger.warning("client.list_jira_projects error err=%s", exc)
1253
1397
  return []
1254
1398
 
1399
+ async def find_jira_project_by_key(self, key: str) -> dict | None:
1400
+ """The project whose `key` matches, case-insensitively, or None.
1401
+
1402
+ The verification step before filing tickets: a key that no longer resolves
1403
+ means the board was renamed or deleted, and creating N tickets against it
1404
+ would fail N times with nothing naming the cause. Matched locally over the
1405
+ full list because the gateway exposes no `?key=` filter.
1406
+ """
1407
+ wanted = str(key or "").strip().casefold()
1408
+ if not wanted:
1409
+ return None
1410
+ for record in await self.list_jira_projects(all_=True):
1411
+ candidate = record.get("key")
1412
+ if isinstance(candidate, str) and candidate.strip().casefold() == wanted:
1413
+ return record
1414
+ return None
1415
+
1255
1416
  async def add_comment(self, task_id: str, body: str) -> bool:
1256
1417
  """POST /tasks-ml/{id}/comments — add a comment to a ticket (best-effort).
1257
1418
 
@@ -326,6 +326,102 @@ def runtime_allowed_ips() -> list[str]:
326
326
  return _parse_ip_list(_load_raw().get("allowed_ips", ""))
327
327
 
328
328
 
329
+ # ── Feature flags / tunables ──────────────────────────────────────────
330
+ # Read through the same precedence as every other override here — process env,
331
+ # then ``~/.agentos/.env`` (or a CWD ``.env``), then config.toml, then the
332
+ # built-in default. Read at CALL time, never at import, so a flag flipped on a
333
+ # box applies to the next run without restarting the daemon or the UI server.
334
+
335
+ _TRUTHY = frozenset({"1", "true", "yes", "on", "enabled"})
336
+ _FALSY = frozenset({"0", "false", "no", "off", "disabled"})
337
+
338
+
339
+ def setting(key: str, default: str = "") -> str:
340
+ """Raw string value of a setting, or ``default`` when unset everywhere.
341
+
342
+ ``key`` is the env-var spelling (``AGENTOS_TIERED_ROUTING``); the config.toml
343
+ lookup uses its lowercase form without the ``AGENTOS_`` prefix
344
+ (``tiered_routing``), matching how ``allowed_tools`` / ``allowed_ips``
345
+ already read.
346
+ """
347
+ for source in (os.environ.get(key, ""), _dotenv_value(key)):
348
+ value = (source or "").strip()
349
+ if value:
350
+ return value
351
+ toml_key = key[len("AGENTOS_"):].lower() if key.startswith("AGENTOS_") else key.lower()
352
+ try:
353
+ value = str(_load_raw().get(toml_key, "") or "").strip()
354
+ except Exception: # noqa: BLE001 — a config hiccup must never break a run
355
+ value = ""
356
+ return value or default
357
+
358
+
359
+ def flag(key: str, default: bool = False) -> bool:
360
+ """A boolean feature flag. Anything unrecognised falls back to ``default``.
361
+
362
+ Every new tiered-routing behaviour is gated on one of these and every one
363
+ defaults to **False**, so a machine with nothing configured behaves exactly
364
+ as it does today. That is the primary acceptance criterion for the feature,
365
+ not a nicety: an unset flag must be indistinguishable from the flag not
366
+ existing.
367
+ """
368
+ raw = setting(key).lower()
369
+ if raw in _TRUTHY:
370
+ return True
371
+ if raw in _FALSY:
372
+ return False
373
+ return default
374
+
375
+
376
+ def int_setting(key: str, default: int) -> int:
377
+ """An integer tunable, tolerant of junk (returns ``default`` on any garbage)."""
378
+ try:
379
+ return int(float(setting(key, str(default))))
380
+ except (TypeError, ValueError):
381
+ return default
382
+
383
+
384
+ def float_setting(key: str, default: float) -> float:
385
+ """A float tunable, tolerant of junk."""
386
+ try:
387
+ return float(setting(key, str(default)))
388
+ except (TypeError, ValueError):
389
+ return default
390
+
391
+
392
+ # The flag names, in one place so a reader can see the whole opt-in surface.
393
+ #: Master switch for the escalation ladder (sdk/router.py). Off → the ladder is
394
+ #: never consulted and run_claude_local is called exactly as it is today.
395
+ FLAG_TIERED_ROUTING = "AGENTOS_TIERED_ROUTING"
396
+ #: Journal + resume-triple assembly (sdk/journal.py, sdk/resume.py). Independent
397
+ #: of the ladder on purpose: phase 2 proves recovery on the subscription tier
398
+ #: ALONE before any tier switching exists.
399
+ FLAG_JOURNAL = "AGENTOS_JOURNAL"
400
+ #: Write the generated PRD to ``docs/prd/<KEY>.md`` in the repo.
401
+ FLAG_PRD_FILE = "AGENTOS_PRD_FILE"
402
+ #: Supervise ``graphify watch`` instead of relying on the debounce.
403
+ FLAG_GRAPH_WATCH = "AGENTOS_GRAPHIFY_WATCH"
404
+ #: Cheap-tier mini agent (sdk/tiers/). Requires backend reachability.
405
+ FLAG_CHEAP_TIER = "AGENTOS_CHEAP_TIER"
406
+ #: Pass ``--resume`` across a provider change. Unverified; see the plan's §12.
407
+ FLAG_CROSS_PROVIDER_RESUME = "AGENTOS_CROSS_PROVIDER_RESUME"
408
+
409
+
410
+ def tiered_routing_enabled() -> bool:
411
+ """True when the escalation ladder may run at all."""
412
+ return flag(FLAG_TIERED_ROUTING)
413
+
414
+
415
+ def journal_enabled() -> bool:
416
+ """True when writes are journalled and resume prompts may be assembled.
417
+
418
+ Implied by the ladder: escalating without a journal would lose the task
419
+ context the ladder exists to preserve, so turning the ladder on turns this
420
+ on whether or not it was named.
421
+ """
422
+ return flag(FLAG_JOURNAL) or tiered_routing_enabled()
423
+
424
+
329
425
  def add_allowed_ip(ip: str) -> list[str]:
330
426
  """Whitelist an IP/CIDR at RUNTIME (persisted to config.toml ``allowed_ips``).
331
427