devops-bot-sdk 1.7.20__tar.gz → 1.7.36__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 (290) hide show
  1. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/PKG-INFO +3 -1
  2. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/devops_bot_sdk.egg-info/PKG-INFO +3 -1
  3. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/devops_bot_sdk.egg-info/SOURCES.txt +8 -0
  4. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/devops_bot_sdk.egg-info/requires.txt +3 -0
  5. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/pyproject.toml +6 -0
  6. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/__init__.py +2 -2
  7. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/agentd.py +35 -12
  8. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/agents/schedules.py +56 -0
  9. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/cli.py +14 -9
  10. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/config.py +3 -0
  11. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/faqs.py +33 -1
  12. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/git_ops.py +73 -14
  13. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/graph_recon.py +4 -1
  14. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/graphify.py +17 -3
  15. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/helpdesk_kb.md +101 -3
  16. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ipc/handlers.py +106 -16
  17. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/log_colors.py +20 -0
  18. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/run_auto.py +48 -3
  19. devops_bot_sdk-1.7.36/sdk/schedule_timeline.py +171 -0
  20. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ticket_pools.py +88 -6
  21. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ui/api.py +18 -2
  22. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ui/dashboard.py +34 -9
  23. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ui/models.py +2 -0
  24. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ui/static/agents.html +1 -0
  25. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ui/static/app.js +897 -110
  26. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ui/static/chat.html +1 -0
  27. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ui/static/custom-agents.html +1 -0
  28. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ui/static/default-agents.html +1 -0
  29. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ui/static/faqs.html +1 -0
  30. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ui/static/help.html +6 -5
  31. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ui/static/index.html +1 -0
  32. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ui/static/report.html +1 -0
  33. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ui/static/runs.html +2 -1
  34. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ui/static/schedules.html +34 -13
  35. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ui/static/setup.html +1 -0
  36. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ui/static/styles.css +309 -4
  37. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ui/static/tickets.html +100 -26
  38. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_agent_run_ticket.py +62 -0
  39. devops_bot_sdk-1.7.36/tests/test_graph_off_by_default.py +27 -0
  40. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_graph_recon.py +6 -0
  41. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_graphify_build.py +6 -0
  42. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_graphify_hook_install.py +6 -0
  43. devops_bot_sdk-1.7.36/tests/test_pool_pr_modes.py +143 -0
  44. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_prd_artifact.py +6 -0
  45. devops_bot_sdk-1.7.36/tests/test_quiet_run_log.py +40 -0
  46. devops_bot_sdk-1.7.36/tests/test_schedule_timeline.py +115 -0
  47. devops_bot_sdk-1.7.36/tests/test_theme_switch.py +35 -0
  48. devops_bot_sdk-1.7.36/tests/test_ticket_detail_view.py +51 -0
  49. devops_bot_sdk-1.7.36/tests/test_ui_no_native_dialogs.py +18 -0
  50. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/README.md +0 -0
  51. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
  52. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
  53. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/devops_bot_sdk.egg-info/top_level.txt +0 -0
  54. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/activity/__init__.py +0 -0
  55. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/activity/classifier.py +0 -0
  56. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/activity/collectors/__init__.py +0 -0
  57. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/activity/collectors/common.py +0 -0
  58. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/activity/collectors/linux_auditd.py +0 -0
  59. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/activity/collectors/macos_eslogger.py +0 -0
  60. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/activity/collectors/windows_eventlog.py +0 -0
  61. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/activity/models.py +0 -0
  62. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/activity/os_setup.py +0 -0
  63. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/activity/recorder.py +0 -0
  64. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/activity/redact.py +0 -0
  65. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/activity/settings.py +0 -0
  66. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/activity/shipper.py +0 -0
  67. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/activity/spool.py +0 -0
  68. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/agent_sharing.py +0 -0
  69. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/agents/__init__.py +0 -0
  70. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/agents/activity.py +0 -0
  71. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/agents/builder.py +0 -0
  72. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/agents/chat.py +0 -0
  73. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/agents/connectors.py +0 -0
  74. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/agents/engines/__init__.py +0 -0
  75. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/agents/engines/claude_code.py +0 -0
  76. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/agents/engines/http.py +0 -0
  77. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/agents/engines/mcp.py +0 -0
  78. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/agents/engines/n8n.py +0 -0
  79. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/agents/engines/shell.py +0 -0
  80. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/agents/integrations.py +0 -0
  81. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/agents/inventory.py +0 -0
  82. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/agents/library/__init__.py +0 -0
  83. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/agents/manifest.py +0 -0
  84. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/agents/prompt_studio.py +0 -0
  85. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/agents/prompt_template.py +0 -0
  86. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/agents/prompt_vars.py +0 -0
  87. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/agents/run_log.py +0 -0
  88. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/agents/secrets.py +0 -0
  89. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/agents/ticket_run.py +0 -0
  90. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/agents/usage.py +0 -0
  91. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/agents/varstore.py +0 -0
  92. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/browser_profile.py +0 -0
  93. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/claude_usage.py +0 -0
  94. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/client.py +0 -0
  95. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/collectors/__init__.py +0 -0
  96. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/collectors/files.py +0 -0
  97. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/collectors/process.py +0 -0
  98. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/collectors/screenshot.py +0 -0
  99. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/crucial.py +0 -0
  100. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/custom_agents.py +0 -0
  101. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/deferred_tickets.py +0 -0
  102. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/departments.py +0 -0
  103. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/design_verify/__init__.py +0 -0
  104. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/design_verify/assets.py +0 -0
  105. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/design_verify/bootstrap.py +0 -0
  106. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/design_verify/browser.py +0 -0
  107. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/design_verify/compare.py +0 -0
  108. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/design_verify/loop.py +0 -0
  109. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/estimator.py +0 -0
  110. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/exceptions.py +0 -0
  111. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/fallback_handoff.py +0 -0
  112. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/firewall.py +0 -0
  113. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/formats.py +0 -0
  114. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/github_fetch.py +0 -0
  115. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/github_ingest.py +0 -0
  116. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/help_actions.py +0 -0
  117. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/helpdesk.py +0 -0
  118. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/hooks/__init__.py +0 -0
  119. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/hooks/crucial_guard.py +0 -0
  120. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/instructions_template.py +0 -0
  121. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/intake.py +0 -0
  122. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ipc/__init__.py +0 -0
  123. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ipc/electron_bridge.py +0 -0
  124. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/journal.py +0 -0
  125. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/local_exec.py +0 -0
  126. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/memory_guard.py +0 -0
  127. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/model_policy.py +0 -0
  128. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/models/__init__.py +0 -0
  129. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/models/envelope.py +0 -0
  130. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/models/requests.py +0 -0
  131. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/models/responses.py +0 -0
  132. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/models/snapshots.py +0 -0
  133. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/phase_timer.py +0 -0
  134. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/plan_run.py +0 -0
  135. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/platform_compat.py +0 -0
  136. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/prd_progress.py +0 -0
  137. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/proof_readers.py +0 -0
  138. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/providers.py +0 -0
  139. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/py.typed +0 -0
  140. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/resume.py +0 -0
  141. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/router.py +0 -0
  142. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/schedule_tickets.py +0 -0
  143. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/scheduler.py +0 -0
  144. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/scope_plan.py +0 -0
  145. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/self_report.py +0 -0
  146. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/self_report_local.py +0 -0
  147. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/sse.py +0 -0
  148. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/swagger.py +0 -0
  149. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/task_file.py +0 -0
  150. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/task_types.py +0 -0
  151. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/test.py +0 -0
  152. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/test_pipeline.py +0 -0
  153. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ticket_board.py +0 -0
  154. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ticket_bulk.py +0 -0
  155. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ticket_fields.py +0 -0
  156. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ticket_plan.py +0 -0
  157. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/tickets.py +0 -0
  158. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/tiers/__init__.py +0 -0
  159. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/tiers/mini_agent.py +0 -0
  160. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ui/__init__.py +0 -0
  161. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ui/health.py +0 -0
  162. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ui/ip_allowlist.py +0 -0
  163. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ui/jobs.py +0 -0
  164. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ui/runner.py +0 -0
  165. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ui/security.py +0 -0
  166. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ui/server.py +0 -0
  167. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ui/service.py +0 -0
  168. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ui/session.py +0 -0
  169. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ui/setup_manager.py +0 -0
  170. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/ui/swagger.py +0 -0
  171. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/updater.py +0 -0
  172. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/url_access.py +0 -0
  173. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/vps_health.py +0 -0
  174. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/vps_identity.py +0 -0
  175. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/sdk/write_hook.py +0 -0
  176. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/setup.cfg +0 -0
  177. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_activity_claude_runs.py +0 -0
  178. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_activity_collectors.py +0 -0
  179. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_activity_core.py +0 -0
  180. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_activity_live_tracking.py +0 -0
  181. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_activity_macos.py +0 -0
  182. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_activity_os_setup.py +0 -0
  183. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_activity_pipeline.py +0 -0
  184. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_agent_activity.py +0 -0
  185. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_agent_activity_tracking.py +0 -0
  186. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_agent_chat_features.py +0 -0
  187. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_agent_crud_cli.py +0 -0
  188. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_agent_listing.py +0 -0
  189. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_agent_prompt_async.py +0 -0
  190. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_agent_prompt_models.py +0 -0
  191. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_agent_schedules.py +0 -0
  192. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_agent_sharing.py +0 -0
  193. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_agent_workspace_settings.py +0 -0
  194. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_agents_connectors_builder.py +0 -0
  195. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_allowed_tools_config.py +0 -0
  196. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_approval_park.py +0 -0
  197. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_auto_agent_dispatch.py +0 -0
  198. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_auto_ticket_queue.py +0 -0
  199. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_browser_profile.py +0 -0
  200. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_cheap_tier.py +0 -0
  201. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_claude_usage_gate.py +0 -0
  202. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_client_jira_projects.py +0 -0
  203. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_config_token_diagnostics.py +0 -0
  204. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_configure_token_owner.py +0 -0
  205. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_console_encoding.py +0 -0
  206. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_crash_recovery.py +0 -0
  207. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_create_safe_retry.py +0 -0
  208. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_deferred_tickets.py +0 -0
  209. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_departments.py +0 -0
  210. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_done_transition.py +0 -0
  211. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_epic_decompose_run.py +0 -0
  212. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_epic_inherited_agent_id.py +0 -0
  213. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_epic_session_isolation.py +0 -0
  214. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_estimator.py +0 -0
  215. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_fallback_cli.py +0 -0
  216. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_fallback_cli_upgrade.py +0 -0
  217. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_faqs.py +0 -0
  218. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_firewall_open_on_update.py +0 -0
  219. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_formats.py +0 -0
  220. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_git_action.py +0 -0
  221. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_github_fetch.py +0 -0
  222. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_github_ingest.py +0 -0
  223. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_health.py +0 -0
  224. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_help_actions.py +0 -0
  225. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_help_attachments.py +0 -0
  226. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_help_questions.py +0 -0
  227. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_helpdesk.py +0 -0
  228. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_infographic_data.py +0 -0
  229. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_instructions_template.py +0 -0
  230. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_intake_job_outcome.py +0 -0
  231. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_intake_plan_review.py +0 -0
  232. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_journal.py +0 -0
  233. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_limit_fallback_handoff.py +0 -0
  234. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_local_exec_provider.py +0 -0
  235. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_log_colors_backlog.py +0 -0
  236. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_mcp_route_contract.py +0 -0
  237. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_memory_guard.py +0 -0
  238. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_model_policy.py +0 -0
  239. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_panel_help.py +0 -0
  240. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_panel_ticket_schedules.py +0 -0
  241. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_parallel_tickets.py +0 -0
  242. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_phase_timer.py +0 -0
  243. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_plan_new_command.py +0 -0
  244. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_plan_run_activity.py +0 -0
  245. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_pr_identity.py +0 -0
  246. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_project_path_resolution.py +0 -0
  247. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_proof_readers.py +0 -0
  248. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_providers.py +0 -0
  249. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_recon_session_isolation.py +0 -0
  250. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_resume.py +0 -0
  251. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_router.py +0 -0
  252. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_run_auto_agent_only.py +0 -0
  253. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_run_outputs.py +0 -0
  254. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_run_verdict_is_honest.py +0 -0
  255. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_schedule_api.py +0 -0
  256. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_schedule_outputs_and_plans.py +0 -0
  257. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_schedule_tickets.py +0 -0
  258. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_scheduler.py +0 -0
  259. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_scheduler_transient_retry.py +0 -0
  260. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_self_report.py +0 -0
  261. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_service_token_warning.py +0 -0
  262. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_stale_panel_restart.py +0 -0
  263. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_swagger_command.py +0 -0
  264. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_task_file_and_plan_run.py +0 -0
  265. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_task_file_format_doc.py +0 -0
  266. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_ticket_board.py +0 -0
  267. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_ticket_bulk.py +0 -0
  268. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_ticket_cli.py +0 -0
  269. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_ticket_fields.py +0 -0
  270. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_ticket_plan.py +0 -0
  271. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_ticket_pools.py +0 -0
  272. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_ticket_run_resolve_by_id.py +0 -0
  273. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_ticket_schedule_and_agent.py +0 -0
  274. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_ticket_speed_fixes.py +0 -0
  275. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_tier_flag_parity.py +0 -0
  276. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_ui_gate.py +0 -0
  277. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_ui_lifespan.py +0 -0
  278. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_update_timer.py +0 -0
  279. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_updater_extras.py +0 -0
  280. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_updater_ignores_cwd.py +0 -0
  281. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_updater_pep668.py +0 -0
  282. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_updater_reports_the_truth.py +0 -0
  283. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_url_access.py +0 -0
  284. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_vps_health.py +0 -0
  285. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_vps_only_reporting.py +0 -0
  286. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_windows_loop_identity.py +0 -0
  287. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_windows_panel_task.py +0 -0
  288. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_windows_stop_and_pidfile.py +0 -0
  289. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/tests/test_windows_task_paths.py +0 -0
  290. {devops_bot_sdk-1.7.20 → devops_bot_sdk-1.7.36}/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.7.20
3
+ Version: 1.7.36
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
@@ -22,6 +22,8 @@ Requires-Dist: fastapi>=0.110; extra == "ui"
22
22
  Requires-Dist: uvicorn>=0.29; extra == "ui"
23
23
  Requires-Dist: psutil>=5.9; extra == "ui"
24
24
  Requires-Dist: python-multipart>=0.0.9; extra == "ui"
25
+ Provides-Extra: ui-e2e
26
+ Requires-Dist: playwright>=1.45; extra == "ui-e2e"
25
27
  Provides-Extra: all
26
28
  Requires-Dist: Pillow>=10.0; extra == "all"
27
29
  Requires-Dist: psutil>=5.9; extra == "all"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devops-bot-sdk
3
- Version: 1.7.20
3
+ Version: 1.7.36
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
@@ -22,6 +22,8 @@ Requires-Dist: fastapi>=0.110; extra == "ui"
22
22
  Requires-Dist: uvicorn>=0.29; extra == "ui"
23
23
  Requires-Dist: psutil>=5.9; extra == "ui"
24
24
  Requires-Dist: python-multipart>=0.0.9; extra == "ui"
25
+ Provides-Extra: ui-e2e
26
+ Requires-Dist: playwright>=1.45; extra == "ui-e2e"
25
27
  Provides-Extra: all
26
28
  Requires-Dist: Pillow>=10.0; extra == "all"
27
29
  Requires-Dist: psutil>=5.9; extra == "all"
@@ -50,6 +50,7 @@ sdk/resume.py
50
50
  sdk/router.py
51
51
  sdk/run_auto.py
52
52
  sdk/schedule_tickets.py
53
+ sdk/schedule_timeline.py
53
54
  sdk/scheduler.py
54
55
  sdk/scope_plan.py
55
56
  sdk/self_report.py
@@ -206,6 +207,7 @@ tests/test_formats.py
206
207
  tests/test_git_action.py
207
208
  tests/test_github_fetch.py
208
209
  tests/test_github_ingest.py
210
+ tests/test_graph_off_by_default.py
209
211
  tests/test_graph_recon.py
210
212
  tests/test_graphify_build.py
211
213
  tests/test_graphify_hook_install.py
@@ -231,11 +233,13 @@ tests/test_parallel_tickets.py
231
233
  tests/test_phase_timer.py
232
234
  tests/test_plan_new_command.py
233
235
  tests/test_plan_run_activity.py
236
+ tests/test_pool_pr_modes.py
234
237
  tests/test_pr_identity.py
235
238
  tests/test_prd_artifact.py
236
239
  tests/test_project_path_resolution.py
237
240
  tests/test_proof_readers.py
238
241
  tests/test_providers.py
242
+ tests/test_quiet_run_log.py
239
243
  tests/test_recon_session_isolation.py
240
244
  tests/test_resume.py
241
245
  tests/test_router.py
@@ -245,6 +249,7 @@ tests/test_run_verdict_is_honest.py
245
249
  tests/test_schedule_api.py
246
250
  tests/test_schedule_outputs_and_plans.py
247
251
  tests/test_schedule_tickets.py
252
+ tests/test_schedule_timeline.py
248
253
  tests/test_scheduler.py
249
254
  tests/test_scheduler_transient_retry.py
250
255
  tests/test_self_report.py
@@ -253,9 +258,11 @@ tests/test_stale_panel_restart.py
253
258
  tests/test_swagger_command.py
254
259
  tests/test_task_file_and_plan_run.py
255
260
  tests/test_task_file_format_doc.py
261
+ tests/test_theme_switch.py
256
262
  tests/test_ticket_board.py
257
263
  tests/test_ticket_bulk.py
258
264
  tests/test_ticket_cli.py
265
+ tests/test_ticket_detail_view.py
259
266
  tests/test_ticket_fields.py
260
267
  tests/test_ticket_plan.py
261
268
  tests/test_ticket_pools.py
@@ -265,6 +272,7 @@ tests/test_ticket_speed_fixes.py
265
272
  tests/test_tier_flag_parity.py
266
273
  tests/test_ui_gate.py
267
274
  tests/test_ui_lifespan.py
275
+ tests/test_ui_no_native_dialogs.py
268
276
  tests/test_update_timer.py
269
277
  tests/test_updater_extras.py
270
278
  tests/test_updater_ignores_cwd.py
@@ -22,3 +22,6 @@ fastapi>=0.110
22
22
  uvicorn>=0.29
23
23
  psutil>=5.9
24
24
  python-multipart>=0.0.9
25
+
26
+ [ui-e2e]
27
+ playwright>=1.45
@@ -41,6 +41,9 @@ ui = [
41
41
  # raises at request time without it, so it belongs in [ui]
42
42
  # alongside fastapi rather than in an optional extra.
43
43
  ]
44
+ ui-e2e = [
45
+ "playwright>=1.45", # tests/ui_e2e — headless-Chromium panel tests (then: python -m playwright install chromium)
46
+ ]
44
47
  all = [
45
48
  "Pillow>=10.0",
46
49
  "psutil>=5.9",
@@ -60,6 +63,9 @@ agentos-swagger = "sdk.swagger:main" # where /docs is + can you reach it
60
63
  Homepage = "https://agentos.io"
61
64
  Repository = "https://github.com/consultancy-outfit/DevOpsBot-SDK"
62
65
 
66
+ [tool.pytest.ini_options]
67
+ markers = ["ui_e2e: headless-Chromium control-panel tests (need the ui-e2e extra; skipped without it)"]
68
+
63
69
  [tool.setuptools.dynamic]
64
70
  version = { attr = "sdk.__version__" } # bump sdk/__init__.py → wheel + client + server all follow
65
71
 
@@ -1,6 +1,6 @@
1
1
  """AgentOS Desktop SDK — thin HTTPS/SSE client for the Electron app.
2
2
 
3
- Version: 1.7.20
3
+ Version: 1.7.36
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.7.20" # SINGLE SOURCE OF TRUTH — bump on every change; pyproject,
37
+ __version__ = "1.7.36" # 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
@@ -359,21 +359,41 @@ async def _dispatch_agent_run(
359
359
  manifest.integrations, available, env=secret_env,
360
360
  )
361
361
 
362
- # EPHEMERAL variables: filled FRESH from the ticket (when one is attached) and/or
363
- # from explicit run-time values — never persisted. Explicit ``extra_variables``
364
- # win over ticket-derived ones. With neither, unresolved {{vars}} are left for
365
- # the agent to ask about or default, exactly as before.
366
- resolved_prompt = manifest.prompt
362
+ # {{variables}}, most specific first: explicit run-time values (UI/API/CLI) win
363
+ # over the attached ticket's fields, which win over the values last saved for
364
+ # this agent (sdk/agents/varstore.py — what the panel's Run used). Nothing here
365
+ # is persisted. A person-started run (auto_approve) that would still leave a
366
+ # placeholder unfilled is REFUSED up front: the agent cannot do the work, so it
367
+ # would only spend a run asking for the values (ticket-instructions-writer,
368
+ # 2026-10-27, got literal {{ticket_key}} / {{repos_root}}).
369
+ from sdk.agents import varstore as agent_varstore
370
+
367
371
  names = extract_variables(manifest.prompt)
368
- if names and (task or extra_variables):
369
- values = values_from_ticket(names, task) if task else {}
372
+ used_inputs: dict[str, str] = {}
373
+ if names:
374
+ saved = {k: v for k, v in agent_varstore.load_vars(manifest.name).items()
375
+ if k in names and str(v).strip()}
376
+ values = dict(saved)
377
+ if task:
378
+ values.update({k: v for k, v in values_from_ticket(names, task).items() if str(v).strip()})
370
379
  if extra_variables:
371
- values = {**values, **{k: str(v) for k, v in extra_variables.items() if str(v).strip()}}
380
+ values.update({k: str(v) for k, v in extra_variables.items() if str(v).strip()})
372
381
  resolved_prompt, missing = substitute_variables(manifest.prompt, values)
373
- if values:
382
+ used_inputs = {k: v for k, v in values.items() if k in names}
383
+ if missing and auto_approve:
384
+ outcome["status"] = "missing_inputs"
385
+ outcome["missing"] = missing
386
+ outcome["error"] = (
387
+ f"'{manifest.name}' needs {', '.join(missing)} — fill "
388
+ f"{'it' if len(missing) == 1 else 'them'} in, or pick a ticket that has "
389
+ f"{'it' if len(missing) == 1 else 'them'}. Nothing was run."
390
+ )
391
+ logger.info("agentd.missing_inputs agent=%s missing=%s", manifest.name, missing)
392
+ return outcome
393
+ if used_inputs:
374
394
  logger.info(
375
- "agentd.vars_filled agent=%s filled=%s missing=%s",
376
- manifest.name, sorted(values), sorted(missing),
395
+ "agentd.vars_filled agent=%s filled=%s from_saved=%s missing=%s",
396
+ manifest.name, sorted(used_inputs), sorted(saved), sorted(missing),
377
397
  )
378
398
  manifest = manifest.model_copy(update={"prompt": resolved_prompt})
379
399
 
@@ -455,9 +475,12 @@ async def _dispatch_agent_run(
455
475
  from sdk.agents.engines import claude_code as claude_code_engine
456
476
  from sdk.agents import run_log
457
477
 
458
- _kind = "ticket" if task else "scheduled"
478
+ # A person pressed Run (auto_approve) → "run"; only an unattended
479
+ # trigger without a ticket is "scheduled".
480
+ _kind = "ticket" if task else ("run" if auto_approve else "scheduled")
459
481
  _log_path = run_log.start_run(
460
482
  manifest.name, scope="custom", engine=manifest.engine, kind=_kind,
483
+ variables=used_inputs or None,
461
484
  )
462
485
  run_log.log_line(_log_path, f"task_id={task_id}")
463
486
 
@@ -411,6 +411,7 @@ def mark_result(schedule_id: str, *, ok: bool, status: str = "",
411
411
  if schedule is None:
412
412
  return None
413
413
  schedule["last_status"] = status or ("ok" if ok else "failed")
414
+ _append_history(schedule, ok=ok, output_file=output_file)
414
415
  if output_file:
415
416
  schedule["last_output_file"] = output_file
416
417
  if delivered_to:
@@ -431,6 +432,61 @@ def mark_result(schedule_id: str, *, ok: bool, status: str = "",
431
432
  return schedule
432
433
 
433
434
 
435
+ # Every outcome mark_result records, one JSON line each, so the Schedules page can
436
+ # show what ran before the latest run. Not ``*.json``: list_schedules globs those.
437
+ HISTORY_NAME = "history.jsonl"
438
+ HISTORY_MAX_LINES = 5000
439
+
440
+
441
+ def history_path() -> Path:
442
+ return SCHEDULES_DIR / HISTORY_NAME
443
+
444
+
445
+ def _append_history(schedule: dict, *, ok: bool, output_file: str = "") -> None:
446
+ """Best-effort: a history failure never affects the run or the schedule."""
447
+ row = {
448
+ "at": datetime.now(timezone.utc).isoformat(timespec="seconds"),
449
+ "fired_at": schedule.get("last_fire_at") or "",
450
+ "schedule_id": schedule.get("schedule_id") or "",
451
+ "agent": schedule.get("agent") or "",
452
+ "kind": schedule.get("kind") or "agent",
453
+ "ok": bool(ok),
454
+ "status": schedule.get("last_status") or "",
455
+ "output_file": output_file or "",
456
+ }
457
+ try:
458
+ path = history_path()
459
+ path.parent.mkdir(parents=True, exist_ok=True)
460
+ with path.open("a", encoding="utf-8") as fh:
461
+ fh.write(json.dumps(row, ensure_ascii=False) + "\n")
462
+ if path.stat().st_size > HISTORY_MAX_LINES * 400: # trim rarely, keep the newest
463
+ lines = path.read_text(encoding="utf-8").splitlines()[-HISTORY_MAX_LINES:]
464
+ path.write_text("\n".join(lines) + "\n", encoding="utf-8")
465
+ except OSError as exc:
466
+ logger.debug("schedules.history_write_failed err=%s", exc)
467
+
468
+
469
+ def read_history(since: datetime | None = None) -> list[dict]:
470
+ """Recorded outcomes, oldest first (optionally only those at/after ``since``)."""
471
+ out: list[dict] = []
472
+ try:
473
+ lines = history_path().read_text(encoding="utf-8").splitlines()
474
+ except OSError:
475
+ return out
476
+ for line in lines:
477
+ try:
478
+ row = json.loads(line)
479
+ except ValueError:
480
+ continue
481
+ if not isinstance(row, dict):
482
+ continue
483
+ at = _parse_iso(row.get("fired_at")) or _parse_iso(row.get("at"))
484
+ if at is None or (since is not None and at < since):
485
+ continue
486
+ out.append(row)
487
+ return out
488
+
489
+
434
490
  def for_agent(agent: str) -> list[dict]:
435
491
  return [s for s in list_schedules() if s.get("agent") == agent]
436
492
 
@@ -1264,12 +1264,12 @@ _POOL_USAGE = """usage: agentos pool <command> — up to 50 tickets you run to
1264
1264
 
1265
1265
  agentos pool list # every pool, its size and last run
1266
1266
  agentos pool show NAME # run order, each ticket's summary/status, instructions
1267
- agentos pool create NAME KEY… [--instructions TEXT] [--parallel N] # new pool (keys in run order)
1267
+ agentos pool create NAME KEY… [--instructions TEXT] [--parallel N] [--pr-mode per_ticket|stacked|single] # new pool (keys in run order)
1268
1268
  agentos pool add NAME KEY… [--at POS] # add tickets (at the end, or at position POS)
1269
1269
  agentos pool remove NAME KEY… # take tickets out
1270
1270
  agentos pool move NAME KEY POS # move one ticket to position POS (1 = first)
1271
1271
  agentos pool order NAME KEY… # these first, in this order; the rest follow
1272
- agentos pool set NAME [--instructions TEXT | --clear-instructions] [--parallel N] [--rename NEW]
1272
+ agentos pool set NAME [--instructions TEXT | --clear-instructions] [--parallel N] [--pr-mode MODE] [--rename NEW]
1273
1273
  agentos pool run NAME [--parallel N] # run it now (= agentos-auto pool NAME)
1274
1274
  agentos pool delete NAME # remove the pool (tickets are untouched)
1275
1275
 
@@ -1306,8 +1306,11 @@ def _pool_opts(args: list[str], valued: tuple[str, ...], bare: tuple[str, ...] =
1306
1306
 
1307
1307
  def _pool_print(pool: dict, info: dict | None = None) -> None:
1308
1308
  from sdk import log_colors
1309
+ from sdk.ticket_pools import PR_MODE_LABELS as TP_LABELS
1309
1310
  paint = log_colors.colorize if log_colors.enabled() else (lambda x: x)
1310
- print(f"pool {pool['name']} — {len(pool['keys'])} ticket(s), up to {pool.get('parallel')} at once"
1311
+ mode = pool.get("pr_mode") or "per_ticket"
1312
+ at_once = f"up to {pool.get('parallel')} at once" if mode == "per_ticket" else "one at a time"
1313
+ print(f"pool {pool['name']} — {len(pool['keys'])} ticket(s), {at_once}, {TP_LABELS.get(mode, mode)}"
1311
1314
  + (f", last run {pool['last_run_at']}" if pool.get("last_run_at") else ""))
1312
1315
  width = max(len(k) for k in pool["keys"]) if pool["keys"] else 0
1313
1316
  for n, k in enumerate(pool["keys"], 1):
@@ -1358,11 +1361,13 @@ def _pool_command(args: list[str]) -> None:
1358
1361
  _pool_print(pool, info)
1359
1362
  return
1360
1363
  if sub == "create":
1361
- pos, opts = _pool_opts(rest, ("--instructions", "--parallel"))
1364
+ pos, opts = _pool_opts(rest, ("--instructions", "--parallel", "--pr-mode"))
1362
1365
  if len(pos) < 2:
1363
- print("usage: agentos pool create NAME KEY… [--instructions TEXT] [--parallel N]")
1366
+ print("usage: agentos pool create NAME KEY… [--instructions TEXT] [--parallel N] "
1367
+ "[--pr-mode per_ticket|stacked|single]")
1364
1368
  sys.exit(1)
1365
- pool = TP.create(pos[0], pos[1:], opts.get("--instructions", ""), _parallel(opts))
1369
+ pool = TP.create(pos[0], pos[1:], opts.get("--instructions", ""), _parallel(opts),
1370
+ opts.get("--pr-mode"))
1366
1371
  shown = f'"{pool["name"]}"' if " " in pool["name"] else pool["name"]
1367
1372
  print(f"✓ created pool {pool['name']} with {len(pool['keys'])} ticket(s) — "
1368
1373
  f"run it: agentos pool run {shown}")
@@ -1389,15 +1394,15 @@ def _pool_command(args: list[str]) -> None:
1389
1394
  sys.exit(1)
1390
1395
  pool = TP.order(rest[0], rest[1:])
1391
1396
  elif sub == "set":
1392
- pos, opts = _pool_opts(rest, ("--instructions", "--parallel", "--rename"),
1397
+ pos, opts = _pool_opts(rest, ("--instructions", "--parallel", "--rename", "--pr-mode"),
1393
1398
  ("--clear-instructions",))
1394
1399
  if len(pos) != 1 or not opts:
1395
1400
  print("usage: agentos pool set NAME [--instructions TEXT | --clear-instructions] "
1396
- "[--parallel N] [--rename NEW]")
1401
+ "[--parallel N] [--pr-mode per_ticket|stacked|single] [--rename NEW]")
1397
1402
  sys.exit(1)
1398
1403
  instr = "" if opts.get("--clear-instructions") else opts.get("--instructions")
1399
1404
  pool = TP.update(pos[0], instructions=instr, parallel=_parallel(opts),
1400
- rename=opts.get("--rename"))
1405
+ rename=opts.get("--rename"), pr_mode=opts.get("--pr-mode"))
1401
1406
  elif sub == "delete":
1402
1407
  if not rest:
1403
1408
  print("usage: agentos pool delete NAME")
@@ -407,6 +407,9 @@ FLAG_TIERED_ROUTING = "AGENTOS_TIERED_ROUTING"
407
407
  FLAG_JOURNAL = "AGENTOS_JOURNAL"
408
408
  #: Write the generated PRD to ``docs/prd/<KEY>.md`` in the repo.
409
409
  FLAG_PRD_FILE = "AGENTOS_PRD_FILE"
410
+ #: The knowledge graph (graphify + graph recon). OFF by default since 1.7.27;
411
+ #: ``AGENTOS_GRAPHIFY=1`` (env or config.toml) turns it back on.
412
+ FLAG_GRAPHIFY = "AGENTOS_GRAPHIFY"
410
413
  #: Supervise ``graphify watch`` instead of relying on the debounce.
411
414
  FLAG_GRAPH_WATCH = "AGENTOS_GRAPHIFY_WATCH"
412
415
  #: Cheap-tier mini agent (sdk/tiers/). Requires backend reachability.
@@ -7,7 +7,25 @@ FAQS: list[tuple[str, str, str]] = [
7
7
  ("Basics", "Does my code leave my machine?",
8
8
  "No. The server plans and gates; cloning, coding, commits and PRs run locally with claude."),
9
9
  ("Basics", "What do I need before installing?",
10
- "Python 3.11+, git, the claude CLI logged in, and your co_ API token (agentos configure)."),
10
+ "Tick every line:\n"
11
+ "1. **Python 3.11+** — `python3 --version`\n"
12
+ "2. **git** — `git --version`\n"
13
+ "3. **GitHub over SSH** — your SSH key on your GitHub account; `ssh -T git@github.com` greets you by name\n"
14
+ "4. **Added to the repositories** — read on DevOpsBot-SDK (to install and update it), write on every repo "
15
+ "your tickets work on, each cloned where its Human Instructions say\n"
16
+ "5. **A way to open PRs** — the platform's GitHub token, or `gh auth login`\n"
17
+ "6. **The claude CLI, logged in** — `claude setup-token`; no claude? the AI Draftsman CLI: "
18
+ "`agentos fallback install`, then `agentos fallback login`\n"
19
+ "7. **Your co_ API token** — `agentos configure`\n"
20
+ "Then `agentos doctor` checks the lot."),
21
+ ("Basics", "Can I run tickets without the claude CLI?",
22
+ "Yes. Install and log in the AI Draftsman CLI (agentos fallback install, agentos fallback login --browser; "
23
+ "check with agentos fallback status --verify). The loop refuses to start only when neither claude nor ad "
24
+ "is ready; with both, ad takes over whenever claude is missing, logged out or out of usage."),
25
+ ("Basics", "Cloning, pushing or opening PRs fails. What do I check?",
26
+ "ssh -T git@github.com must greet you by name (add your SSH key to GitHub if not); you must be added to the "
27
+ "repo with write access; gh auth status must be logged in unless the platform gives the SDK a GitHub token. "
28
+ "A repo whose origin is not on GitHub gets its branch pushed but no PR, so the ticket goes to Backlog."),
11
29
  ("Updates", "How does the SDK stay up to date?",
12
30
  "Every machine checks for a new version every 6 hours by itself — a systemd timer on Linux, a "
13
31
  "Task Scheduler task on Windows, a launchd agent on macOS — and catches up after downtime. "
@@ -55,6 +73,20 @@ FAQS: list[tuple[str, str, str]] = [
55
73
  "A very old console refuses colour mode, or NO_COLOR is set. Try AGENTOS_COLOR=always."),
56
74
  ("Help", "Where do settings and logs live?",
57
75
  "~/.agentos: config.toml, auto.log, pools, schedules and per-run logs. agentos doctor bundles them for an admin."),
76
+ ("Tickets", "How do I move tickets between statuses?",
77
+ "Any status can move to any other. On the Tickets page tick them and press Move to ▾, change Status in "
78
+ "a ticket's side panel, or drag cards on the Board tab. From the CLI: agentos ticket bulk-update KEY… --status Done."),
79
+ ("Plans & pools", "Can pooled tickets build on each other, or share one PR?",
80
+ "Yes — the pool's PR mode: agentos pool set NAME --pr-mode stacked (each ticket branches from the previous "
81
+ "one and its PR targets it) or --pr-mode single (one branch and one PR for the whole pool). Both run one "
82
+ "ticket at a time, in pool order."),
83
+ ("Agents", "Why did my agent say \"Not run — fill in …\"?",
84
+ "Its prompt needs inputs that had no value — not in the form, not on the picked ticket, and not saved from "
85
+ "its last run. Nothing was run and nothing was spent. Fill the inputs or pick a ticket."),
86
+ ("Running work", "The log shows only a few lines while a ticket runs. Is it stuck?",
87
+ "No — the log shows each file edited and one progress line a minute, and \"still working … quiet for Nm\" "
88
+ "during long silent steps such as a test run. Every step is in the ticket's claude log; "
89
+ "AGENTOS_VERBOSE_STEPS=1 brings them back. agentos-auto status shows the current ticket."),
58
90
  ]
59
91
 
60
92
 
@@ -80,9 +80,9 @@ _TICKET_BRANCH_RE = re.compile(r"^[A-Za-z][A-Za-z0-9]{1,9}-\d+$")
80
80
 
81
81
 
82
82
  def _is_work_branch(name: str) -> bool:
83
- """True when `name` is one of OUR generated work branches (feature/* or a
84
- bare ticket id) — i.e. never a legitimate base."""
85
- return name.startswith("feature/") or bool(_TICKET_BRANCH_RE.match(name))
83
+ """True when `name` is one of OUR generated work branches (feature/*, a pool's
84
+ pool/* or a bare ticket id) — i.e. never a legitimate base."""
85
+ return name.startswith(("feature/", "pool/")) or bool(_TICKET_BRANCH_RE.match(name))
86
86
 
87
87
 
88
88
  async def detect_default_branch(path: str, env: dict) -> str:
@@ -163,9 +163,16 @@ async def start_branch(
163
163
  github_token: str | None,
164
164
  base: str | None = None,
165
165
  branch_name: str | None = None,
166
+ continue_branch: bool = False,
166
167
  ) -> dict | None:
167
168
  """Create + check out a feature branch off the resolved base.
168
169
 
170
+ ``continue_branch`` (a one-PR ticket pool, after its first ticket): check out
171
+ the EXISTING ``branch_name`` as it stands — local, else ``origin/`` — and bring
172
+ it up to date, instead of re-cutting it from ``base``; ``base`` is returned
173
+ unchanged so the PR keeps its target. Re-cutting would throw away the earlier
174
+ tickets' commits.
175
+
169
176
  Branch name:
170
177
  - `branch_name` given (an explicit name parsed from the Jira
171
178
  humanInstructions or a configured template, e.g. the exact ticket id
@@ -196,6 +203,17 @@ async def start_branch(
196
203
  return None
197
204
 
198
205
  explicit_name = (branch_name or "").strip().lstrip("/")
206
+ if continue_branch and explicit_name:
207
+ await _run(["git", "fetch", "origin"], path, env, timeout=180.0)
208
+ rc, _, _ = await _run(["git", "checkout", explicit_name], path, env)
209
+ if rc != 0:
210
+ rc, _, _ = await _run(["git", "checkout", "-B", explicit_name, f"origin/{explicit_name}"], path, env)
211
+ if rc == 0:
212
+ await _run(["git", "pull", "--ff-only", "origin", explicit_name], path, env, timeout=120.0)
213
+ pr_base = (base or "").strip() or await detect_default_branch(path, env)
214
+ logger.info("git_ops.branch | %s continued (pool), PR base %s", explicit_name, pr_base)
215
+ return {"branch": explicit_name, "base": pr_base}
216
+ # Not there yet (the pool's first ticket): cut it like any named branch.
199
217
  if explicit_name:
200
218
  # Honour the exact name the human/convention asked for (e.g. "CD-72").
201
219
  branch = explicit_name
@@ -212,15 +230,20 @@ async def start_branch(
212
230
  # local ref of that name, then to a bare branch, if the remote lacks it.
213
231
  await _run(["git", "fetch", "origin", explicit], path, env, timeout=120.0)
214
232
  resolved = "main" if explicit == branch else explicit
215
- rc, _, _ = await _run(["git", "checkout", "-B", branch, f"origin/{resolved}"], path, env)
216
- if rc != 0:
217
- rc, _, _ = await _run(["git", "checkout", "-B", branch, resolved], path, env)
218
- if rc != 0:
233
+ refs = [r for r in (f"origin/{resolved}", resolved) if await _ref_exists(path, env, r)]
234
+ if not refs:
235
+ # The base does not exist anywhere (a typo, or a brand-new repo): cut
236
+ # from HEAD, and say which branch that really is.
219
237
  rc, _, _ = await _run(["git", "checkout", "-B", branch], path, env)
220
- if rc != 0:
238
+ if rc != 0:
239
+ return None
240
+ logger.warning("git_ops.branch | base %s not found — %s cut from the current checkout", resolved, branch)
241
+ return {"branch": branch, "base": resolved, "note": f"base {resolved} not found — cut from the current checkout"}
242
+ info = await _checkout_from(path, env, branch, refs)
243
+ if info is None:
221
244
  return None
222
245
  logger.info("git_ops.branch | %s ← explicit base %s", branch, resolved)
223
- return {"branch": branch, "base": resolved}
246
+ return {"branch": branch, "base": resolved, **info}
224
247
 
225
248
  # Default: base = the CURRENT checked-out branch (the user's working branch).
226
249
  rc, cur, _ = await _run(["git", "rev-parse", "--abbrev-ref", "HEAD"], path, env)
@@ -238,13 +261,49 @@ async def start_branch(
238
261
  resolved = await detect_default_branch(path, env)
239
262
  if resolved == branch:
240
263
  resolved = "main"
241
- rc, _, _ = await _run(["git", "checkout", "-B", branch, f"origin/{resolved}"], path, env)
242
- if rc != 0:
243
- rc, _, _ = await _run(["git", "checkout", "-B", branch], path, env)
244
- if rc != 0:
264
+ refs = [r for r in (f"origin/{resolved}", resolved) if await _ref_exists(path, env, r)]
265
+ info = await _checkout_from(path, env, branch, refs) if refs else None
266
+ if info is None:
267
+ # Never fall back to the stale work branch that is checked out: that is
268
+ # how one ticket's half-finished changes end up in the next one's PR.
269
+ logger.warning("git_ops.branch | %s: could not cut from %s (HEAD was %r)", branch, resolved, cur or "detached")
245
270
  return None
246
271
  logger.info("git_ops.branch | %s ← default base %s (HEAD was %r)", branch, resolved, cur or "detached")
247
- return {"branch": branch, "base": resolved}
272
+ return {"branch": branch, "base": resolved, **info}
273
+
274
+
275
+ async def _ref_exists(path: str, env: dict, ref: str) -> bool:
276
+ rc, _, _ = await _run(["git", "rev-parse", "--verify", "--quiet", ref + "^{commit}"], path, env)
277
+ return rc == 0
278
+
279
+
280
+ async def _checkout_from(path: str, env: dict, branch: str, refs: list[str]) -> dict | None:
281
+ """Check ``branch`` out fresh from the first of ``refs`` that works → {} or
282
+ {"stashed": msg}; None when none does.
283
+
284
+ The usual reason a checkout fails is uncommitted changes in the way —
285
+ typically a previous ticket's half-finished work. Those are STASHED (kept, with
286
+ a message naming this branch; ``git stash list`` shows them) and the checkout
287
+ retried, instead of quietly cutting the branch from whatever is checked out —
288
+ which would carry those changes, and a stale codebase, into this ticket's PR.
289
+ """
290
+ for ref in refs:
291
+ rc, _, _ = await _run(["git", "checkout", "-B", branch, ref], path, env)
292
+ if rc == 0:
293
+ return {}
294
+ if not await has_local_changes(path):
295
+ return None
296
+ msg = f"agentos: uncommitted changes set aside before {branch}"
297
+ rc, _, err = await _run(["git", "stash", "push", "--include-untracked", "-m", msg], path, env)
298
+ if rc != 0:
299
+ logger.warning("git_ops.stash_failed %s: %s", branch, err[:200])
300
+ return None
301
+ logger.warning("git_ops.branch | stashed uncommitted changes before %s", branch)
302
+ for ref in refs:
303
+ rc, _, _ = await _run(["git", "checkout", "-B", branch, ref], path, env)
304
+ if rc == 0:
305
+ return {"stashed": msg}
306
+ return None
248
307
 
249
308
 
250
309
  async def _remote_owner_repo(path: str, env: dict) -> tuple[str, str] | None:
@@ -75,7 +75,10 @@ _CAMEL_RE = re.compile(r"[A-Z]+(?![a-z])|[A-Z][a-z0-9]*|[a-z0-9]+")
75
75
 
76
76
 
77
77
  def _disabled() -> bool:
78
- return bool(os.getenv("AGENTOS_NO_GRAPH_RECON"))
78
+ if os.getenv("AGENTOS_NO_GRAPH_RECON"):
79
+ return True
80
+ from sdk import graphify # no graph (off by default) → no graph recon
81
+ return not graphify.enabled()
79
82
 
80
83
 
81
84
  def _split_identifier(word: str) -> list[str]:
@@ -6,8 +6,11 @@ hook**, so the agent queries the graph instead of loading whole files — keepin
6
6
  large projects inside the context window.
7
7
 
8
8
  Everything here is best-effort and never raises: if graphify is unavailable or a
9
- step fails, the run continues on plain `claude`. Disable with
10
- ``AGENTOS_NO_GRAPHIFY=1``.
9
+ step fails, the run continues on plain `claude`.
10
+
11
+ OFF by default (1.7.27): nothing is installed, built or queried unless
12
+ ``AGENTOS_GRAPHIFY=1`` is set (environment or ``config.toml``). The code stays so
13
+ it can be switched back on; ``AGENTOS_NO_GRAPHIFY=1`` still forces it off.
11
14
 
12
15
  Per-project flow (called once before each local `claude` run):
13
16
  ensure_installed() → auto-install `graphifyy` via uv → pipx → pip if missing
@@ -45,8 +48,19 @@ _SKILL_MARKER = _AGENTOS_DIR / ".graphify_skill_installed"
45
48
  _STATE_FILE = _AGENTOS_DIR / "graphify_state.json"
46
49
 
47
50
 
51
+ def enabled() -> bool:
52
+ """True only when switched on (``AGENTOS_GRAPHIFY=1``) and not forced off."""
53
+ if os.getenv("AGENTOS_NO_GRAPHIFY"):
54
+ return False
55
+ try:
56
+ from sdk import config
57
+ return config.flag(config.FLAG_GRAPHIFY)
58
+ except Exception: # noqa: BLE001 — a config hiccup leaves it off
59
+ return False
60
+
61
+
48
62
  def _disabled() -> bool:
49
- return bool(os.getenv("AGENTOS_NO_GRAPHIFY"))
63
+ return not enabled()
50
64
 
51
65
 
52
66
  def is_available() -> bool: