devops-bot-sdk 1.6.66__tar.gz → 1.6.68__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 (230) hide show
  1. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/PKG-INFO +1 -1
  2. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
  3. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/devops_bot_sdk.egg-info/SOURCES.txt +1 -0
  4. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/__init__.py +2 -2
  5. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/ui/service.py +94 -22
  6. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_update_timer.py +2 -0
  7. devops_bot_sdk-1.6.68/tests/test_windows_task_paths.py +172 -0
  8. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/README.md +0 -0
  9. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
  10. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
  11. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/devops_bot_sdk.egg-info/requires.txt +0 -0
  12. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/devops_bot_sdk.egg-info/top_level.txt +0 -0
  13. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/pyproject.toml +0 -0
  14. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/activity/__init__.py +0 -0
  15. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/activity/classifier.py +0 -0
  16. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/activity/collectors/__init__.py +0 -0
  17. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/activity/collectors/common.py +0 -0
  18. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/activity/collectors/linux_auditd.py +0 -0
  19. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/activity/collectors/macos_eslogger.py +0 -0
  20. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/activity/collectors/windows_eventlog.py +0 -0
  21. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/activity/models.py +0 -0
  22. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/activity/os_setup.py +0 -0
  23. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/activity/recorder.py +0 -0
  24. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/activity/redact.py +0 -0
  25. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/activity/settings.py +0 -0
  26. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/activity/shipper.py +0 -0
  27. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/activity/spool.py +0 -0
  28. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/agentd.py +0 -0
  29. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/agents/__init__.py +0 -0
  30. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/agents/activity.py +0 -0
  31. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/agents/builder.py +0 -0
  32. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/agents/chat.py +0 -0
  33. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/agents/connectors.py +0 -0
  34. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/agents/engines/__init__.py +0 -0
  35. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/agents/engines/claude_code.py +0 -0
  36. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/agents/engines/http.py +0 -0
  37. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/agents/engines/mcp.py +0 -0
  38. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/agents/engines/n8n.py +0 -0
  39. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/agents/engines/shell.py +0 -0
  40. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/agents/integrations.py +0 -0
  41. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/agents/inventory.py +0 -0
  42. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/agents/library/__init__.py +0 -0
  43. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/agents/manifest.py +0 -0
  44. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/agents/prompt_studio.py +0 -0
  45. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/agents/prompt_template.py +0 -0
  46. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/agents/prompt_vars.py +0 -0
  47. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/agents/run_log.py +0 -0
  48. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/agents/schedules.py +0 -0
  49. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/agents/secrets.py +0 -0
  50. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/agents/ticket_run.py +0 -0
  51. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/agents/usage.py +0 -0
  52. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/agents/varstore.py +0 -0
  53. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/browser_profile.py +0 -0
  54. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/cli.py +0 -0
  55. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/client.py +0 -0
  56. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/collectors/__init__.py +0 -0
  57. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/collectors/files.py +0 -0
  58. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/collectors/process.py +0 -0
  59. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/collectors/screenshot.py +0 -0
  60. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/config.py +0 -0
  61. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/crucial.py +0 -0
  62. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/custom_agents.py +0 -0
  63. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/departments.py +0 -0
  64. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/design_verify/__init__.py +0 -0
  65. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/design_verify/assets.py +0 -0
  66. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/design_verify/bootstrap.py +0 -0
  67. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/design_verify/browser.py +0 -0
  68. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/design_verify/compare.py +0 -0
  69. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/design_verify/loop.py +0 -0
  70. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/estimator.py +0 -0
  71. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/exceptions.py +0 -0
  72. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/firewall.py +0 -0
  73. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/formats.py +0 -0
  74. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/git_ops.py +0 -0
  75. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/github_fetch.py +0 -0
  76. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/github_ingest.py +0 -0
  77. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/graph_recon.py +0 -0
  78. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/graphify.py +0 -0
  79. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/hooks/__init__.py +0 -0
  80. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/hooks/crucial_guard.py +0 -0
  81. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/intake.py +0 -0
  82. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/ipc/__init__.py +0 -0
  83. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/ipc/electron_bridge.py +0 -0
  84. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/ipc/handlers.py +0 -0
  85. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/journal.py +0 -0
  86. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/local_exec.py +0 -0
  87. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/memory_guard.py +0 -0
  88. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/models/__init__.py +0 -0
  89. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/models/envelope.py +0 -0
  90. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/models/requests.py +0 -0
  91. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/models/responses.py +0 -0
  92. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/models/snapshots.py +0 -0
  93. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/phase_timer.py +0 -0
  94. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/plan_run.py +0 -0
  95. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/platform_compat.py +0 -0
  96. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/prd_progress.py +0 -0
  97. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/proof_readers.py +0 -0
  98. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/providers.py +0 -0
  99. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/py.typed +0 -0
  100. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/resume.py +0 -0
  101. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/router.py +0 -0
  102. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/run_auto.py +0 -0
  103. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/scheduler.py +0 -0
  104. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/self_report.py +0 -0
  105. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/self_report_local.py +0 -0
  106. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/sse.py +0 -0
  107. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/swagger.py +0 -0
  108. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/task_file.py +0 -0
  109. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/task_types.py +0 -0
  110. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/test.py +0 -0
  111. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/test_pipeline.py +0 -0
  112. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/ticket_fields.py +0 -0
  113. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/tickets.py +0 -0
  114. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/tiers/__init__.py +0 -0
  115. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/tiers/mini_agent.py +0 -0
  116. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/ui/__init__.py +0 -0
  117. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/ui/api.py +0 -0
  118. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/ui/dashboard.py +0 -0
  119. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/ui/health.py +0 -0
  120. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/ui/ip_allowlist.py +0 -0
  121. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/ui/jobs.py +0 -0
  122. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/ui/models.py +0 -0
  123. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/ui/runner.py +0 -0
  124. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/ui/security.py +0 -0
  125. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/ui/server.py +0 -0
  126. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/ui/session.py +0 -0
  127. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/ui/setup_manager.py +0 -0
  128. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/ui/static/agents.html +0 -0
  129. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/ui/static/app.js +0 -0
  130. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/ui/static/chat.html +0 -0
  131. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/ui/static/custom-agents.html +0 -0
  132. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/ui/static/default-agents.html +0 -0
  133. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/ui/static/index.html +0 -0
  134. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/ui/static/report.html +0 -0
  135. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/ui/static/runs.html +0 -0
  136. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/ui/static/schedules.html +0 -0
  137. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/ui/static/setup.html +0 -0
  138. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/ui/static/styles.css +0 -0
  139. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/ui/static/tickets.html +0 -0
  140. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/ui/swagger.py +0 -0
  141. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/updater.py +0 -0
  142. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/url_access.py +0 -0
  143. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/vendor/aidraftsman-cli-0.1.0.tgz +0 -0
  144. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/vps_health.py +0 -0
  145. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/sdk/write_hook.py +0 -0
  146. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/setup.cfg +0 -0
  147. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_activity_collectors.py +0 -0
  148. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_activity_core.py +0 -0
  149. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_activity_macos.py +0 -0
  150. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_activity_os_setup.py +0 -0
  151. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_activity_pipeline.py +0 -0
  152. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_agent_activity.py +0 -0
  153. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_agent_activity_tracking.py +0 -0
  154. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_agent_chat_features.py +0 -0
  155. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_agent_crud_cli.py +0 -0
  156. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_agent_listing.py +0 -0
  157. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_agent_prompt_async.py +0 -0
  158. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_agent_prompt_models.py +0 -0
  159. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_agent_run_ticket.py +0 -0
  160. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_agent_schedules.py +0 -0
  161. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_agent_workspace_settings.py +0 -0
  162. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_agents_connectors_builder.py +0 -0
  163. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_allowed_tools_config.py +0 -0
  164. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_auto_agent_dispatch.py +0 -0
  165. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_browser_profile.py +0 -0
  166. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_cheap_tier.py +0 -0
  167. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_client_jira_projects.py +0 -0
  168. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_config_token_diagnostics.py +0 -0
  169. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_configure_token_owner.py +0 -0
  170. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_console_encoding.py +0 -0
  171. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_crash_recovery.py +0 -0
  172. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_departments.py +0 -0
  173. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_done_transition.py +0 -0
  174. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_epic_decompose_run.py +0 -0
  175. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_epic_inherited_agent_id.py +0 -0
  176. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_epic_session_isolation.py +0 -0
  177. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_estimator.py +0 -0
  178. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_fallback_cli.py +0 -0
  179. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_firewall_open_on_update.py +0 -0
  180. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_formats.py +0 -0
  181. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_git_action.py +0 -0
  182. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_github_fetch.py +0 -0
  183. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_github_ingest.py +0 -0
  184. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_graph_recon.py +0 -0
  185. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_graphify_build.py +0 -0
  186. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_graphify_hook_install.py +0 -0
  187. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_health.py +0 -0
  188. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_intake_job_outcome.py +0 -0
  189. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_intake_plan_review.py +0 -0
  190. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_journal.py +0 -0
  191. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_local_exec_provider.py +0 -0
  192. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_mcp_route_contract.py +0 -0
  193. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_memory_guard.py +0 -0
  194. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_phase_timer.py +0 -0
  195. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_plan_new_command.py +0 -0
  196. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_plan_run_activity.py +0 -0
  197. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_pr_identity.py +0 -0
  198. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_prd_artifact.py +0 -0
  199. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_project_path_resolution.py +0 -0
  200. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_proof_readers.py +0 -0
  201. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_providers.py +0 -0
  202. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_recon_session_isolation.py +0 -0
  203. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_resume.py +0 -0
  204. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_router.py +0 -0
  205. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_run_auto_agent_only.py +0 -0
  206. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_run_outputs.py +0 -0
  207. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_run_verdict_is_honest.py +0 -0
  208. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_schedule_api.py +0 -0
  209. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_schedule_outputs_and_plans.py +0 -0
  210. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_scheduler.py +0 -0
  211. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_self_report.py +0 -0
  212. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_service_token_warning.py +0 -0
  213. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_stale_panel_restart.py +0 -0
  214. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_swagger_command.py +0 -0
  215. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_task_file_and_plan_run.py +0 -0
  216. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_task_file_format_doc.py +0 -0
  217. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_ticket_cli.py +0 -0
  218. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_ticket_fields.py +0 -0
  219. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_ticket_run_resolve_by_id.py +0 -0
  220. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_ticket_speed_fixes.py +0 -0
  221. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_tier_flag_parity.py +0 -0
  222. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_ui_gate.py +0 -0
  223. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_ui_lifespan.py +0 -0
  224. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_updater_extras.py +0 -0
  225. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_updater_pep668.py +0 -0
  226. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_updater_reports_the_truth.py +0 -0
  227. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_url_access.py +0 -0
  228. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_vps_health.py +0 -0
  229. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/tests/test_windows_loop_identity.py +0 -0
  230. {devops_bot_sdk-1.6.66 → devops_bot_sdk-1.6.68}/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.66
3
+ Version: 1.6.68
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.66
3
+ Version: 1.6.68
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
@@ -224,4 +224,5 @@ tests/test_updater_reports_the_truth.py
224
224
  tests/test_url_access.py
225
225
  tests/test_vps_health.py
226
226
  tests/test_windows_loop_identity.py
227
+ tests/test_windows_task_paths.py
227
228
  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.66
3
+ Version: 1.6.68
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.66" # SINGLE SOURCE OF TRUTH — bump on every change; pyproject,
37
+ __version__ = "1.6.68" # 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
@@ -33,25 +33,65 @@ WIN_TASK = "AgentOS Control Panel" # Windows scheduled-task name
33
33
  WIN_UPDATE_TASK = "AgentOS Self-Update" # Windows counterpart of agentos-update.timer
34
34
 
35
35
 
36
+ def _script_dirs() -> list[Path]:
37
+ """Where pip puts console scripts for THIS interpreter, most specific first.
38
+
39
+ Next to the interpreter covers a venv (``bin/`` / ``Scripts\\`` holds both
40
+ ``python`` and the scripts). It does NOT cover a regular Windows install, where
41
+ ``python.exe`` sits in the install root and the scripts in its ``Scripts\\``
42
+ subfolder — ``sysconfig``'s ``scripts`` path is the one that knows. The user
43
+ scheme covers a ``pip install --user``.
44
+ """
45
+ import sysconfig
46
+
47
+ dirs = [Path(sys.executable).parent]
48
+ for args in (("scripts",), ("scripts", f"{os.name}_user")):
49
+ try:
50
+ p = sysconfig.get_path(*args)
51
+ except KeyError: # scheme not defined on this platform
52
+ continue
53
+ if p and Path(p) not in dirs:
54
+ dirs.append(Path(p))
55
+ return dirs
56
+
57
+
58
+ def _console_script(base: str) -> str | None:
59
+ """ABSOLUTE path to this SDK's ``base`` console script, or ``None`` if not found."""
60
+ name = f"{base}.exe" if IS_WINDOWS else base
61
+ for d in _script_dirs():
62
+ cand = d / name
63
+ if cand.exists():
64
+ return str(cand)
65
+ found = shutil.which(name)
66
+ return str(Path(found).resolve()) if found else None
67
+
68
+
36
69
  def _exe() -> str:
37
70
  """ABSOLUTE path to the ``agentos-ui`` console script.
38
71
 
39
- Must be absolute: a service manager (systemd/launchd) does NOT search the
40
- user's PATH for ExecStart, so a bare ``agentos-ui`` fails with 203/EXEC when
41
- the script lives somewhere like ``~/.local/bin`` (a ``pip --user`` install) —
42
- the common case on a VPS. Resolve, in order:
43
- 1. next to the running interpreter (venv installs),
44
- 2. on the current PATH (finds ``~/.local/bin`` at install time),
45
- 3. bare name as a last resort (kept only so install never hard-fails).
72
+ Must be absolute: a service manager (systemd/launchd/Task Scheduler) does NOT
73
+ search the user's PATH, so a bare ``agentos-ui`` fails with 203/EXEC when the
74
+ script lives somewhere like ``~/.local/bin`` (a ``pip --user`` install) — the
75
+ common case on a VPS — and with 0x80070002 (file not found) on Windows, where
76
+ it lives in ``<python>\\Scripts``. Resolved via :func:`_console_script`; the
77
+ bare name is a last resort kept so a systemd install never hard-fails. The
78
+ Windows installers refuse it (see :func:`_require_absolute`).
46
79
  """
47
- name = "agentos-ui.exe" if IS_WINDOWS else "agentos-ui"
48
- cand = Path(sys.executable).parent / name
49
- if cand.exists():
50
- return str(cand)
51
- found = shutil.which(name)
52
- if found:
53
- return str(Path(found).resolve())
54
- return name
80
+ return _console_script("agentos-ui") or ("agentos-ui.exe" if IS_WINDOWS else "agentos-ui")
81
+
82
+
83
+ def _require_absolute(exe: str, task: str) -> str | None:
84
+ """Refusal message when ``exe`` is not an absolute path, else ``None``.
85
+
86
+ A scheduled task with a bare program name is created without complaint and then
87
+ never starts — Last Result 0x80070002, no panel, no activity trail, nothing on
88
+ screen. Refusing here turns that silence into a message the updater prints.
89
+ """
90
+ if Path(exe).is_absolute():
91
+ return None
92
+ return (f"not creating task '{task}': could not find {exe} for this Python "
93
+ f"({sys.executable}). Reinstall the SDK with that interpreter and re-run "
94
+ f"`agentos-auto update`.")
55
95
 
56
96
 
57
97
  def _run(argv: list[str]) -> tuple[int, str]:
@@ -169,10 +209,7 @@ def _auto_exe() -> str:
169
209
  minimal environment, and picking up a different venv's console script is how
170
210
  an updater ends up upgrading the wrong installation.
171
211
  """
172
- candidate = Path(sys.executable).parent / ("agentos-auto.exe" if IS_WINDOWS else "agentos-auto")
173
- if candidate.exists():
174
- return str(candidate)
175
- return shutil.which("agentos-auto") or "agentos-auto"
212
+ return _console_script("agentos-auto") or ("agentos-auto.exe" if IS_WINDOWS else "agentos-auto")
176
213
 
177
214
 
178
215
  def update_timer_installed() -> bool:
@@ -374,7 +411,11 @@ def _uninstall_launchd() -> tuple[bool, str]:
374
411
  # ── Windows (Scheduled Task) ────────────────────────────────────────────────
375
412
 
376
413
  def _install_schtask(host: str, port: int) -> tuple[bool, str]:
377
- tr = f'"{_exe()}" --host {host} --port {port} --no-browser'
414
+ exe = _exe()
415
+ refusal = _require_absolute(exe, WIN_TASK)
416
+ if refusal:
417
+ return False, refusal
418
+ tr = f'"{exe}" --host {host} --port {port} --no-browser'
378
419
  rc, out = _run(["schtasks", "/Create", "/TN", WIN_TASK, "/TR", tr,
379
420
  "/SC", "ONLOGON", "/RL", "HIGHEST", "/F"])
380
421
  if rc != 0:
@@ -440,7 +481,11 @@ def _install_update_schtask() -> tuple[bool, str]:
440
481
  """
441
482
  import tempfile
442
483
 
443
- xml = _update_task_xml(_auto_exe(), str(Path.home()))
484
+ exe = _auto_exe()
485
+ refusal = _require_absolute(exe, WIN_UPDATE_TASK)
486
+ if refusal:
487
+ return False, refusal
488
+ xml = _update_task_xml(exe, str(Path.home()))
444
489
  fd, path = tempfile.mkstemp(suffix=".xml")
445
490
  try:
446
491
  with os.fdopen(fd, "w", encoding="utf-16") as fh: # schtasks /XML wants UTF-16
@@ -459,6 +504,29 @@ def _install_update_schtask() -> tuple[bool, str]:
459
504
  f"daily, randomized, catches up after downtime")
460
505
 
461
506
 
507
+ def _schtask_command(task: str) -> str | None:
508
+ """The program ``task`` runs, from its XML definition; ``None`` if unreadable."""
509
+ import html
510
+
511
+ rc, out = _run(["schtasks", "/Query", "/TN", task, "/XML"])
512
+ if rc != 0:
513
+ return None
514
+ m = re.search(r"<Command>(.*?)</Command>", out, re.S)
515
+ return html.unescape(m.group(1)).strip().strip('"') if m else None
516
+
517
+
518
+ def _schtask_points_nowhere(task: str) -> bool:
519
+ """True when ``task`` exists but runs a program that is not an existing file.
520
+
521
+ Boxes installed before :func:`_script_dirs` existed have a panel task whose
522
+ program is the bare ``agentos-ui.exe``. It shows "Ready", never runs, and the
523
+ ordinary update path ("installed + unchanged") only ever re-runs it — so without
524
+ this check those boxes would stay broken through every update.
525
+ """
526
+ cmd = _schtask_command(task)
527
+ return cmd is not None and not (Path(cmd).is_absolute() and Path(cmd).is_file())
528
+
529
+
462
530
  def _uninstall_schtask() -> tuple[bool, str]:
463
531
  _run(["schtasks", "/Delete", "/TN", WIN_UPDATE_TASK, "/F"])
464
532
  _run(["schtasks", "/End", "/TN", WIN_TASK])
@@ -771,7 +839,7 @@ def ensure(restart_running: bool = False) -> tuple[bool, str]:
771
839
  if not is_installed():
772
840
  ok, msg = install() # create at the current privilege
773
841
  return ok, (f"service created — {msg}" if ok else f"could not create service: {msg}")
774
- if not update_timer_installed():
842
+ if not update_timer_installed() or (IS_WINDOWS and _schtask_points_nowhere(WIN_UPDATE_TASK)):
775
843
  _install_update_timer()
776
844
  if _needs_root_for_existing():
777
845
  return False, ("a system service is installed — run `sudo agentos-ui install` "
@@ -783,6 +851,10 @@ def ensure(restart_running: bool = False) -> tuple[bool, str]:
783
851
  if restart_running:
784
852
  rok, rmsg = refresh()
785
853
  return rok, (_with_token_warning(rmsg) if rok else rmsg)
854
+ if IS_WINDOWS and _schtask_points_nowhere(WIN_TASK):
855
+ rok, rmsg = refresh() # rebuilds the task with an absolute path
856
+ return rok, (_with_token_warning(f"panel task pointed at a missing program — "
857
+ f"rebuilt; {rmsg}") if rok else rmsg)
786
858
  if _is_active():
787
859
  stale = _restart_if_stale()
788
860
  if stale is not None:
@@ -177,6 +177,7 @@ class TestWindowsUpdateTask:
177
177
 
178
178
  def test_creates_the_task_from_a_utf16_xml_file(self, monkeypatch):
179
179
  monkeypatch.setattr(service, "IS_WINDOWS", True)
180
+ monkeypatch.setattr(service, "_auto_exe", lambda: "/py/Scripts/agentos-auto.exe")
180
181
  seen = {}
181
182
 
182
183
  def fake_run(argv):
@@ -194,6 +195,7 @@ class TestWindowsUpdateTask:
194
195
 
195
196
  def test_a_failed_create_is_reported_not_raised(self, monkeypatch):
196
197
  monkeypatch.setattr(service, "IS_WINDOWS", True)
198
+ monkeypatch.setattr(service, "_auto_exe", lambda: "/py/Scripts/agentos-auto.exe")
197
199
  monkeypatch.setattr(service, "_run", lambda argv: (1, "Access is denied."))
198
200
  ok, msg = service._install_update_timer()
199
201
  assert ok is False and "Access is denied." in msg
@@ -0,0 +1,172 @@
1
+ """Windows scheduled tasks must name their program by ABSOLUTE path.
2
+
3
+ The incident: a Windows VPS on a regular (non-venv) Python install —
4
+ ``...\\pythoncore-3.14-64\\python.exe`` with its console scripts in the ``Scripts\\``
5
+ subfolder. ``_exe()`` looked only next to ``python.exe`` and on PATH, found
6
+ neither, and fell back to the bare ``agentos-ui.exe``. ``schtasks /Create``
7
+ accepted that; Task Scheduler does not search PATH, so the panel task sat
8
+ "Ready" with Last Result 0x80070002 (file not found) and never ran — no panel, no
9
+ activity collector, no shipper, so that VPS never sent one activity event. And the
10
+ ordinary update path only re-ran the broken task, so no update ever fixed it.
11
+ """
12
+ from __future__ import annotations
13
+
14
+ import sysconfig
15
+ from pathlib import Path
16
+
17
+ import pytest
18
+
19
+ from sdk.ui import service
20
+
21
+
22
+ @pytest.fixture
23
+ def windows_install(monkeypatch, tmp_path):
24
+ """A regular Windows install: python.exe in the root, scripts in Scripts\\."""
25
+ root = tmp_path / "pythoncore-3.14-64"
26
+ scripts = root / "Scripts"
27
+ scripts.mkdir(parents=True)
28
+ monkeypatch.setattr(service, "IS_WINDOWS", True)
29
+ monkeypatch.setattr(service.sys, "executable", str(root / "python.exe"))
30
+ monkeypatch.setattr(service.shutil, "which", lambda name: None) # Scripts\ not on PATH
31
+
32
+ def fake_get_path(name, scheme=None):
33
+ if scheme is None:
34
+ return str(scripts)
35
+ raise KeyError(scheme)
36
+ monkeypatch.setattr(sysconfig, "get_path", fake_get_path)
37
+ return scripts
38
+
39
+
40
+ class TestResolution:
41
+ def test_finds_the_panel_in_the_scripts_subfolder(self, windows_install):
42
+ (windows_install / "agentos-ui.exe").write_text("")
43
+ assert service._exe() == str(windows_install / "agentos-ui.exe")
44
+
45
+ def test_finds_the_updater_in_the_scripts_subfolder(self, windows_install):
46
+ (windows_install / "agentos-auto.exe").write_text("")
47
+ assert service._auto_exe() == str(windows_install / "agentos-auto.exe")
48
+
49
+ def test_next_to_the_interpreter_still_wins(self, windows_install):
50
+ """A venv keeps python.exe and the scripts together; that copy is THIS SDK's."""
51
+ beside = Path(service.sys.executable).parent / "agentos-ui.exe"
52
+ beside.write_text("")
53
+ (windows_install / "agentos-ui.exe").write_text("")
54
+ assert service._exe() == str(beside)
55
+
56
+ def test_a_user_scheme_install_is_found(self, monkeypatch, windows_install, tmp_path):
57
+ user_scripts = tmp_path / "Roaming" / "Python" / "Python314" / "Scripts"
58
+ user_scripts.mkdir(parents=True)
59
+ (user_scripts / "agentos-ui.exe").write_text("")
60
+
61
+ def fake_get_path(name, scheme=None):
62
+ return str(user_scripts) if (scheme or "").endswith("_user") else str(windows_install)
63
+ monkeypatch.setattr(sysconfig, "get_path", fake_get_path)
64
+ assert service._exe() == str(user_scripts / "agentos-ui.exe")
65
+
66
+ def test_not_found_anywhere_is_the_bare_name(self, windows_install):
67
+ assert service._exe() == "agentos-ui.exe"
68
+
69
+
70
+ class TestRefusesABareProgramName:
71
+ def test_panel_task_is_not_created(self, monkeypatch, windows_install):
72
+ calls = []
73
+ monkeypatch.setattr(service, "_run", lambda argv: (calls.append(argv), (0, ""))[1])
74
+ ok, msg = service._install_schtask("0.0.0.0", 8765)
75
+ assert ok is False
76
+ assert service.WIN_TASK in msg and "agentos-ui.exe" in msg
77
+ assert calls == [] # never reached schtasks /Create
78
+
79
+ def test_update_task_is_not_created(self, monkeypatch, windows_install):
80
+ calls = []
81
+ monkeypatch.setattr(service, "_run", lambda argv: (calls.append(argv), (0, ""))[1])
82
+ ok, msg = service._install_update_schtask()
83
+ assert ok is False and service.WIN_UPDATE_TASK in msg
84
+ assert calls == []
85
+
86
+ def test_panel_task_uses_the_absolute_path_when_found(self, monkeypatch, windows_install):
87
+ exe = windows_install / "agentos-ui.exe"
88
+ exe.write_text("")
89
+ calls = []
90
+ monkeypatch.setattr(service, "_run", lambda argv: (calls.append(argv), (0, ""))[1])
91
+ ok, _ = service._install_schtask("0.0.0.0", 8765)
92
+ assert ok is True
93
+ create = calls[0]
94
+ assert create[:4] == ["schtasks", "/Create", "/TN", service.WIN_TASK]
95
+ assert create[create.index("/TR") + 1].startswith(f'"{exe}" --host 0.0.0.0')
96
+
97
+
98
+ def _task_xml(command: str) -> str:
99
+ return ("<?xml version=\"1.0\" encoding=\"UTF-16\"?>\n<Task><Actions Context=\"Author\">"
100
+ f"<Exec><Command>{command}</Command><Arguments>--host 0.0.0.0</Arguments>"
101
+ "</Exec></Actions></Task>")
102
+
103
+
104
+ class TestDetectsABrokenTask:
105
+ def test_a_bare_program_name_points_nowhere(self, monkeypatch):
106
+ monkeypatch.setattr(service, "_run", lambda argv: (0, _task_xml('"agentos-ui.exe"')))
107
+ assert service._schtask_points_nowhere(service.WIN_TASK) is True
108
+
109
+ def test_an_absolute_path_that_no_longer_exists_points_nowhere(self, monkeypatch, tmp_path):
110
+ gone = tmp_path / "old-venv" / "Scripts" / "agentos-ui.exe"
111
+ monkeypatch.setattr(service, "_run", lambda argv: (0, _task_xml(str(gone))))
112
+ assert service._schtask_points_nowhere(service.WIN_TASK) is True
113
+
114
+ def test_an_existing_absolute_path_is_fine(self, monkeypatch, tmp_path):
115
+ exe = tmp_path / "R&D" / "agentos-ui.exe"
116
+ exe.parent.mkdir()
117
+ exe.write_text("")
118
+ escaped = str(exe).replace("&", "&amp;")
119
+ monkeypatch.setattr(service, "_run", lambda argv: (0, _task_xml(f'"{escaped}"')))
120
+ assert service._schtask_points_nowhere(service.WIN_TASK) is False
121
+
122
+ def test_a_task_that_cannot_be_read_is_not_called_broken(self, monkeypatch):
123
+ """No task, or no permission to read it: not ours to rebuild on a guess."""
124
+ monkeypatch.setattr(service, "_run", lambda argv: (1, "ERROR: cannot find the file"))
125
+ assert service._schtask_points_nowhere(service.WIN_TASK) is False
126
+
127
+
128
+ class TestEnsureRepairsIt:
129
+ @pytest.fixture
130
+ def installed(self, monkeypatch):
131
+ monkeypatch.setattr(service, "IS_WINDOWS", True)
132
+ monkeypatch.setattr(service, "_service_disabled", lambda: False)
133
+ monkeypatch.setattr(service, "is_installed", lambda: True)
134
+ monkeypatch.setattr(service, "update_timer_installed", lambda: True)
135
+ monkeypatch.setattr(service, "_needs_root_for_existing", lambda: False)
136
+ monkeypatch.setattr(service, "_with_token_warning", lambda m: m)
137
+ seen = {"refresh": 0, "timer": 0, "restart": 0}
138
+
139
+ def refresh():
140
+ seen["refresh"] += 1
141
+ return True, "service refreshed"
142
+
143
+ def timer():
144
+ seen["timer"] += 1
145
+ return True, "timer ok"
146
+
147
+ def restart():
148
+ seen["restart"] += 1
149
+ return True, "restarted"
150
+ monkeypatch.setattr(service, "refresh", refresh)
151
+ monkeypatch.setattr(service, "_install_update_timer", timer)
152
+ monkeypatch.setattr(service, "restart", restart)
153
+ monkeypatch.setattr(service, "_is_active", lambda: False)
154
+ return seen
155
+
156
+ def test_a_broken_panel_task_is_rebuilt_without_an_upgrade(self, monkeypatch, installed):
157
+ monkeypatch.setattr(service, "_schtask_points_nowhere", lambda task: task == service.WIN_TASK)
158
+ ok, msg = service.ensure(restart_running=False)
159
+ assert ok is True and "rebuilt" in msg
160
+ assert installed["refresh"] == 1 and installed["restart"] == 0
161
+
162
+ def test_a_broken_update_task_is_recreated(self, monkeypatch, installed):
163
+ monkeypatch.setattr(service, "_schtask_points_nowhere",
164
+ lambda task: task == service.WIN_UPDATE_TASK)
165
+ service.ensure(restart_running=False)
166
+ assert installed["timer"] == 1
167
+
168
+ def test_a_healthy_stopped_task_is_just_started(self, monkeypatch, installed):
169
+ monkeypatch.setattr(service, "_schtask_points_nowhere", lambda task: False)
170
+ service.ensure(restart_running=False)
171
+ assert installed["refresh"] == 0 and installed["timer"] == 0
172
+ assert installed["restart"] == 1