devops-bot-sdk 1.6.44__tar.gz → 1.6.46__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 (221) hide show
  1. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/PKG-INFO +1 -1
  2. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
  3. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/__init__.py +2 -2
  4. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/activity/collectors/windows_eventlog.py +28 -1
  5. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/service.py +87 -3
  6. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_activity_collectors.py +30 -0
  7. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_update_timer.py +94 -0
  8. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/README.md +0 -0
  9. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/devops_bot_sdk.egg-info/SOURCES.txt +0 -0
  10. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
  11. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
  12. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/devops_bot_sdk.egg-info/requires.txt +0 -0
  13. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/devops_bot_sdk.egg-info/top_level.txt +0 -0
  14. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/pyproject.toml +0 -0
  15. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/activity/__init__.py +0 -0
  16. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/activity/classifier.py +0 -0
  17. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/activity/collectors/__init__.py +0 -0
  18. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/activity/collectors/common.py +0 -0
  19. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/activity/collectors/linux_auditd.py +0 -0
  20. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/activity/collectors/macos_eslogger.py +0 -0
  21. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/activity/models.py +0 -0
  22. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/activity/os_setup.py +0 -0
  23. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/activity/recorder.py +0 -0
  24. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/activity/redact.py +0 -0
  25. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/activity/settings.py +0 -0
  26. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/activity/shipper.py +0 -0
  27. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/activity/spool.py +0 -0
  28. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agentd.py +0 -0
  29. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/__init__.py +0 -0
  30. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/activity.py +0 -0
  31. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/builder.py +0 -0
  32. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/chat.py +0 -0
  33. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/connectors.py +0 -0
  34. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/engines/__init__.py +0 -0
  35. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/engines/claude_code.py +0 -0
  36. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/engines/http.py +0 -0
  37. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/engines/mcp.py +0 -0
  38. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/engines/n8n.py +0 -0
  39. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/engines/shell.py +0 -0
  40. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/integrations.py +0 -0
  41. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/inventory.py +0 -0
  42. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/library/__init__.py +0 -0
  43. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/manifest.py +0 -0
  44. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/prompt_studio.py +0 -0
  45. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/prompt_template.py +0 -0
  46. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/prompt_vars.py +0 -0
  47. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/run_log.py +0 -0
  48. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/schedules.py +0 -0
  49. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/secrets.py +0 -0
  50. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/ticket_run.py +0 -0
  51. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/usage.py +0 -0
  52. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/varstore.py +0 -0
  53. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/browser_profile.py +0 -0
  54. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/cli.py +0 -0
  55. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/client.py +0 -0
  56. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/collectors/__init__.py +0 -0
  57. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/collectors/files.py +0 -0
  58. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/collectors/process.py +0 -0
  59. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/collectors/screenshot.py +0 -0
  60. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/config.py +0 -0
  61. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/crucial.py +0 -0
  62. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/departments.py +0 -0
  63. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/design_verify/__init__.py +0 -0
  64. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/design_verify/assets.py +0 -0
  65. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/design_verify/bootstrap.py +0 -0
  66. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/design_verify/browser.py +0 -0
  67. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/design_verify/compare.py +0 -0
  68. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/design_verify/loop.py +0 -0
  69. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/estimator.py +0 -0
  70. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/exceptions.py +0 -0
  71. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/firewall.py +0 -0
  72. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/formats.py +0 -0
  73. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/git_ops.py +0 -0
  74. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/github_fetch.py +0 -0
  75. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/github_ingest.py +0 -0
  76. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/graph_recon.py +0 -0
  77. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/graphify.py +0 -0
  78. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/hooks/__init__.py +0 -0
  79. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/hooks/crucial_guard.py +0 -0
  80. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/intake.py +0 -0
  81. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ipc/__init__.py +0 -0
  82. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ipc/electron_bridge.py +0 -0
  83. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ipc/handlers.py +0 -0
  84. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/journal.py +0 -0
  85. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/local_exec.py +0 -0
  86. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/memory_guard.py +0 -0
  87. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/models/__init__.py +0 -0
  88. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/models/envelope.py +0 -0
  89. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/models/requests.py +0 -0
  90. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/models/responses.py +0 -0
  91. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/models/snapshots.py +0 -0
  92. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/phase_timer.py +0 -0
  93. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/plan_run.py +0 -0
  94. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/platform_compat.py +0 -0
  95. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/prd_progress.py +0 -0
  96. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/proof_readers.py +0 -0
  97. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/providers.py +0 -0
  98. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/py.typed +0 -0
  99. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/resume.py +0 -0
  100. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/router.py +0 -0
  101. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/run_auto.py +0 -0
  102. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/scheduler.py +0 -0
  103. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/self_report.py +0 -0
  104. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/self_report_local.py +0 -0
  105. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/sse.py +0 -0
  106. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/swagger.py +0 -0
  107. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/task_file.py +0 -0
  108. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/task_types.py +0 -0
  109. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/test.py +0 -0
  110. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/test_pipeline.py +0 -0
  111. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ticket_fields.py +0 -0
  112. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/tiers/__init__.py +0 -0
  113. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/tiers/mini_agent.py +0 -0
  114. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/__init__.py +0 -0
  115. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/api.py +0 -0
  116. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/dashboard.py +0 -0
  117. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/health.py +0 -0
  118. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/ip_allowlist.py +0 -0
  119. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/jobs.py +0 -0
  120. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/models.py +0 -0
  121. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/runner.py +0 -0
  122. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/security.py +0 -0
  123. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/server.py +0 -0
  124. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/session.py +0 -0
  125. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/setup_manager.py +0 -0
  126. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/static/agents.html +0 -0
  127. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/static/app.js +0 -0
  128. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/static/chat.html +0 -0
  129. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/static/custom-agents.html +0 -0
  130. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/static/default-agents.html +0 -0
  131. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/static/index.html +0 -0
  132. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/static/report.html +0 -0
  133. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/static/runs.html +0 -0
  134. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/static/schedules.html +0 -0
  135. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/static/setup.html +0 -0
  136. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/static/styles.css +0 -0
  137. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/static/tickets.html +0 -0
  138. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/swagger.py +0 -0
  139. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/updater.py +0 -0
  140. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/url_access.py +0 -0
  141. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/vps_health.py +0 -0
  142. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/write_hook.py +0 -0
  143. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/setup.cfg +0 -0
  144. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_activity_core.py +0 -0
  145. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_activity_macos.py +0 -0
  146. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_activity_os_setup.py +0 -0
  147. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_activity_pipeline.py +0 -0
  148. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_agent_activity.py +0 -0
  149. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_agent_activity_tracking.py +0 -0
  150. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_agent_chat_features.py +0 -0
  151. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_agent_listing.py +0 -0
  152. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_agent_prompt_async.py +0 -0
  153. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_agent_prompt_models.py +0 -0
  154. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_agent_run_ticket.py +0 -0
  155. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_agent_schedules.py +0 -0
  156. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_agent_workspace_settings.py +0 -0
  157. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_agents_connectors_builder.py +0 -0
  158. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_allowed_tools_config.py +0 -0
  159. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_auto_agent_dispatch.py +0 -0
  160. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_browser_profile.py +0 -0
  161. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_cheap_tier.py +0 -0
  162. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_client_jira_projects.py +0 -0
  163. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_config_token_diagnostics.py +0 -0
  164. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_configure_token_owner.py +0 -0
  165. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_console_encoding.py +0 -0
  166. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_crash_recovery.py +0 -0
  167. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_departments.py +0 -0
  168. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_done_transition.py +0 -0
  169. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_epic_decompose_run.py +0 -0
  170. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_epic_inherited_agent_id.py +0 -0
  171. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_epic_session_isolation.py +0 -0
  172. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_estimator.py +0 -0
  173. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_firewall_open_on_update.py +0 -0
  174. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_formats.py +0 -0
  175. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_git_action.py +0 -0
  176. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_github_fetch.py +0 -0
  177. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_github_ingest.py +0 -0
  178. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_graph_recon.py +0 -0
  179. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_graphify_build.py +0 -0
  180. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_graphify_hook_install.py +0 -0
  181. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_health.py +0 -0
  182. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_intake_job_outcome.py +0 -0
  183. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_intake_plan_review.py +0 -0
  184. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_journal.py +0 -0
  185. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_local_exec_provider.py +0 -0
  186. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_mcp_route_contract.py +0 -0
  187. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_memory_guard.py +0 -0
  188. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_phase_timer.py +0 -0
  189. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_plan_new_command.py +0 -0
  190. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_plan_run_activity.py +0 -0
  191. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_pr_identity.py +0 -0
  192. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_prd_artifact.py +0 -0
  193. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_project_path_resolution.py +0 -0
  194. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_proof_readers.py +0 -0
  195. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_providers.py +0 -0
  196. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_recon_session_isolation.py +0 -0
  197. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_resume.py +0 -0
  198. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_router.py +0 -0
  199. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_run_auto_agent_only.py +0 -0
  200. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_run_outputs.py +0 -0
  201. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_run_verdict_is_honest.py +0 -0
  202. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_schedule_api.py +0 -0
  203. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_schedule_outputs_and_plans.py +0 -0
  204. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_scheduler.py +0 -0
  205. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_self_report.py +0 -0
  206. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_service_token_warning.py +0 -0
  207. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_swagger_command.py +0 -0
  208. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_task_file_and_plan_run.py +0 -0
  209. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_task_file_format_doc.py +0 -0
  210. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_ticket_fields.py +0 -0
  211. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_ticket_run_resolve_by_id.py +0 -0
  212. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_ticket_speed_fixes.py +0 -0
  213. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_tier_flag_parity.py +0 -0
  214. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_ui_gate.py +0 -0
  215. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_ui_lifespan.py +0 -0
  216. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_updater_extras.py +0 -0
  217. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_updater_reports_the_truth.py +0 -0
  218. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_url_access.py +0 -0
  219. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_vps_health.py +0 -0
  220. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_windows_loop_identity.py +0 -0
  221. {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/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.44
3
+ Version: 1.6.46
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.44
3
+ Version: 1.6.46
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
  """AgentOS Desktop SDK — thin HTTPS/SSE client for the Electron app.
2
2
 
3
- Version: 1.6.44
3
+ Version: 1.6.46
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.44" # SINGLE SOURCE OF TRUTH — bump on every change; pyproject,
37
+ __version__ = "1.6.46" # 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
@@ -14,6 +14,11 @@ The event parser and the processor are pure; only :class:`WindowsEventLogCollect
14
14
  touches the OS. Processes created by SYSTEM / LOCAL SERVICE / NETWORK SERVICE are
15
15
  skipped by default (``ignore_system``): they are thousands of events an hour of
16
16
  Windows housekeeping that no human or agent did.
17
+
18
+ The collector's own polling is skipped too: every poll runs ``wevtutil.exe`` from the
19
+ panel process (as the panel's user, not SYSTEM), and each of those is itself a 4688.
20
+ Unfiltered, that is ~8,600 "Administrator ran wevtutil" events a day per VPS, each
21
+ poll recording the previous one.
17
22
  """
18
23
  from __future__ import annotations
19
24
 
@@ -47,6 +52,9 @@ logger = logging.getLogger("sdk.activity.collectors.windows")
47
52
  _NS = "{http://schemas.microsoft.com/win/2004/08/events/event}"
48
53
  EVENT_IDS = (4688, 4624, 4634, 4647, 4625)
49
54
  _MAX_PID_CACHE = 20_000
55
+ #: Executables the collector itself spawns to poll the log.
56
+ _OWN_TOOLS = ("wevtutil.exe",)
57
+ _MAX_OWN_PIDS = 1_000
50
58
 
51
59
  #: Logon types that are a PERSON at a keyboard / remote desktop.
52
60
  _INTERACTIVE = {2: SessionKind.CONSOLE, 11: SessionKind.CONSOLE, 10: SessionKind.RDP}
@@ -123,11 +131,14 @@ class WindowsEventProcessor:
123
131
  registry: AgentProcessRegistry | None = None,
124
132
  book: SessionBook | None = None,
125
133
  ignore_system: bool = True,
134
+ self_pid: int | None = None,
126
135
  ) -> None:
127
136
  self._agent_user = agent_user.lower() if agent_user else None
128
137
  self._registry = registry or AgentProcessRegistry()
129
138
  self._book = book or SessionBook()
130
139
  self._ignore_system = ignore_system
140
+ self._self_pid = os.getpid() if self_pid is None else self_pid
141
+ self._own: OrderedDict[int, None] = OrderedDict() # pids of our own polls + children
131
142
  self._ppid: OrderedDict[int, int] = OrderedDict()
132
143
  self._logons: dict[str, str] = {} # logon id → session key
133
144
  self._kinds: dict[str, SessionKind] = {} # logon id → login kind
@@ -202,6 +213,9 @@ class WindowsEventProcessor:
202
213
  d = ev.data
203
214
  pid, ppid = _hex(d.get("NewProcessId")), _hex(d.get("ProcessId"))
204
215
  self._remember_ppid(pid, ppid)
216
+ image = d.get("NewProcessName", "")
217
+ if self._is_own_poll(pid, ppid, image):
218
+ return
205
219
  logon_id = d.get("SubjectLogonId", "").lower()
206
220
  if self._ignore_system and logon_id in _SYSTEM_LOGON_IDS:
207
221
  return
@@ -232,7 +246,6 @@ class WindowsEventProcessor:
232
246
  session_key, started=ev.time, verdict=verdict, os_user=user,
233
247
  ).agent_run_id
234
248
 
235
- image = d.get("NewProcessName", "")
236
249
  recorder.record_event(ActivityEvent(
237
250
  os=Os.WINDOWS, source=Source.WIN_EVENTLOG, category=Category.COMMAND, action="exec",
238
251
  occurredAt=ev.time, sessionKey=session_key,
@@ -243,6 +256,20 @@ class WindowsEventProcessor:
243
256
  metadata={"parent": d.get("ParentProcessName") or None},
244
257
  ))
245
258
 
259
+ def _is_own_poll(self, pid: int, ppid: int, image: str) -> bool:
260
+ """A ``wevtutil`` this process spawned to poll, or a child of one (its ``conhost``).
261
+
262
+ Matched by parent pid AND image name, so anything else the panel spawns —
263
+ an agent run, ``git``, ``claude`` — is still recorded.
264
+ """
265
+ name = image.rsplit("\\", 1)[-1].lower()
266
+ if not ((ppid == self._self_pid and name in _OWN_TOOLS) or ppid in self._own):
267
+ return False
268
+ self._own[pid] = None
269
+ while len(self._own) > _MAX_OWN_PIDS:
270
+ self._own.popitem(last=False)
271
+ return True
272
+
246
273
  def _remember_ppid(self, pid: int, ppid: int) -> None:
247
274
  self._ppid[pid] = ppid
248
275
  self._ppid.move_to_end(pid)
@@ -9,7 +9,9 @@ Per platform:
9
9
  ``loginctl enable-linger`` so it runs without an active login.
10
10
  ``Restart=always`` gives crash-recovery; ``enable`` gives boot-start.
11
11
  * macOS — a launchd LaunchAgent with ``RunAtLoad`` + ``KeepAlive``.
12
- * Windows — a Scheduled Task (``schtasks``) that runs the panel at logon.
12
+ * Windows — a Scheduled Task (``schtasks``) that runs the panel at logon, plus a
13
+ second, daily task that runs ``agentos-auto update`` (the systemd
14
+ timer's counterpart — without it a Windows box never updated itself).
13
15
 
14
16
  Everything is best-effort and returns ``(ok, message)`` so the caller (the
15
17
  ``agentos-ui install`` command and the bootstrap scripts) can report cleanly.
@@ -28,6 +30,7 @@ IS_MAC = sys.platform == "darwin"
28
30
 
29
31
  SERVICE_NAME = "agentos-ui" # systemd unit / launchd label base
30
32
  WIN_TASK = "AgentOS Control Panel" # Windows scheduled-task name
33
+ WIN_UPDATE_TASK = "AgentOS Self-Update" # Windows counterpart of agentos-update.timer
31
34
 
32
35
 
33
36
  def _exe() -> str:
@@ -166,7 +169,7 @@ def _auto_exe() -> str:
166
169
  minimal environment, and picking up a different venv's console script is how
167
170
  an updater ends up upgrading the wrong installation.
168
171
  """
169
- candidate = Path(sys.executable).parent / "agentos-auto"
172
+ candidate = Path(sys.executable).parent / ("agentos-auto.exe" if IS_WINDOWS else "agentos-auto")
170
173
  if candidate.exists():
171
174
  return str(candidate)
172
175
  return shutil.which("agentos-auto") or "agentos-auto"
@@ -174,6 +177,8 @@ def _auto_exe() -> str:
174
177
 
175
178
  def update_timer_installed() -> bool:
176
179
  """Is the daily self-update timer present on this box?"""
180
+ if IS_WINDOWS:
181
+ return _run(["schtasks", "/Query", "/TN", WIN_UPDATE_TASK])[0] == 0
177
182
  if not _systemd_available():
178
183
  return False
179
184
  is_root = hasattr(os, "geteuid") and os.geteuid() == 0
@@ -185,6 +190,8 @@ def update_timer_installed() -> bool:
185
190
  def _install_update_timer() -> tuple[bool, str]:
186
191
  """Install (or refresh) the daily self-update timer. Best-effort by design —
187
192
  the panel must install even on a box where the timer cannot."""
193
+ if IS_WINDOWS:
194
+ return _install_update_schtask()
188
195
  if not _systemd_available():
189
196
  return False, "no systemd — skipping the self-update timer"
190
197
  home = str(Path.home())
@@ -378,7 +385,82 @@ def _install_schtask(host: str, port: int) -> tuple[bool, str]:
378
385
  f"'Run whether user is logged on' + 'Restart on failure' in Task Scheduler.")
379
386
 
380
387
 
388
+ def _update_task_xml(exe: str, home: str) -> str:
389
+ """Task Scheduler XML for the daily self-update — the systemd timer, translated.
390
+
391
+ XML rather than ``schtasks`` flags because the flags cannot express the two
392
+ settings that matter most: ``RandomDelay`` (the fleet must not clone from GitHub
393
+ in the same second) and ``StartWhenAvailable`` (a box that was off at the
394
+ scheduled time updates when it is next up — systemd's ``Persistent=true``).
395
+
396
+ It runs as the installing user with the highest available privileges, same as
397
+ the panel task: ``~/.agentos`` (token, ``.env``) lives in that user's profile, and
398
+ the updater's activity-audit step needs an elevated token to configure auditpol.
399
+ """
400
+ from xml.sax.saxutils import escape
401
+
402
+ delay_min = max(1, UPDATE_RANDOM_DELAY_S // 60)
403
+ return (
404
+ '<?xml version="1.0" encoding="UTF-16"?>\n'
405
+ '<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">\n'
406
+ " <RegistrationInfo><Description>AgentOS SDK self-update (daily)</Description>"
407
+ "</RegistrationInfo>\n"
408
+ " <Triggers>\n"
409
+ " <CalendarTrigger>\n"
410
+ " <StartBoundary>2026-01-01T03:00:00</StartBoundary>\n"
411
+ f" <RandomDelay>PT{delay_min}M</RandomDelay>\n"
412
+ " <ScheduleByDay><DaysInterval>1</DaysInterval></ScheduleByDay>\n"
413
+ " </CalendarTrigger>\n"
414
+ " </Triggers>\n"
415
+ " <Principals><Principal id=\"Author\">"
416
+ "<LogonType>InteractiveToken</LogonType><RunLevel>HighestAvailable</RunLevel>"
417
+ "</Principal></Principals>\n"
418
+ " <Settings>\n"
419
+ " <StartWhenAvailable>true</StartWhenAvailable>\n"
420
+ " <RunOnlyIfNetworkAvailable>true</RunOnlyIfNetworkAvailable>\n"
421
+ " <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>\n"
422
+ " <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>\n"
423
+ " <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>\n"
424
+ " <ExecutionTimeLimit>PT1H</ExecutionTimeLimit>\n"
425
+ " </Settings>\n"
426
+ ' <Actions Context="Author"><Exec>'
427
+ f"<Command>{escape(exe)}</Command><Arguments>update</Arguments>"
428
+ f"<WorkingDirectory>{escape(home)}</WorkingDirectory>"
429
+ "</Exec></Actions>\n"
430
+ "</Task>\n"
431
+ )
432
+
433
+
434
+ def _install_update_schtask() -> tuple[bool, str]:
435
+ """Create (or replace) the daily self-update task. Best-effort, like the timer.
436
+
437
+ A SEPARATE task from the panel's for the same reason the systemd updater is a
438
+ separate unit: the update restarts the panel task, and an updater running inside
439
+ it would be ended part way through its own ``pip install``.
440
+ """
441
+ import tempfile
442
+
443
+ xml = _update_task_xml(_auto_exe(), str(Path.home()))
444
+ fd, path = tempfile.mkstemp(suffix=".xml")
445
+ try:
446
+ with os.fdopen(fd, "w", encoding="utf-16") as fh: # schtasks /XML wants UTF-16
447
+ fh.write(xml)
448
+ rc, out = _run(["schtasks", "/Create", "/TN", WIN_UPDATE_TASK, "/XML", path, "/F"])
449
+ except OSError as exc:
450
+ return False, f"could not write the self-update task: {exc}"
451
+ finally:
452
+ try:
453
+ os.unlink(path)
454
+ except OSError:
455
+ pass
456
+ if rc != 0:
457
+ return False, f"could not create the self-update task:\n{out}"
458
+ return True, (f"self-update task '{WIN_UPDATE_TASK}' created — "
459
+ f"daily, randomized, catches up after downtime")
460
+
461
+
381
462
  def _uninstall_schtask() -> tuple[bool, str]:
463
+ _run(["schtasks", "/Delete", "/TN", WIN_UPDATE_TASK, "/F"])
382
464
  _run(["schtasks", "/End", "/TN", WIN_TASK])
383
465
  rc, out = _run(["schtasks", "/Delete", "/TN", WIN_TASK, "/F"])
384
466
  return (rc == 0), (f"removed scheduled task '{WIN_TASK}'." if rc == 0 else out)
@@ -388,7 +470,9 @@ def _uninstall_schtask() -> tuple[bool, str]:
388
470
 
389
471
  def install(host: str = "0.0.0.0", port: int = 8765) -> tuple[bool, str]:
390
472
  if IS_WINDOWS:
391
- return _install_schtask(host, port)
473
+ ok, msg = _install_schtask(host, port)
474
+ timer_ok, timer_msg = _install_update_timer()
475
+ return ok, f"{msg}\n {'' if timer_ok else 'note: '}{timer_msg}"
392
476
  if IS_MAC:
393
477
  return _install_launchd(host, port)
394
478
  if _systemd_available():
@@ -417,6 +417,36 @@ class TestWindowsProcessing:
417
417
  assert e["sessionKey"] == "agent-run:RUN1" and e["agentRunId"] == "RUN1"
418
418
  assert (e["actorType"], e["actorConfidence"]) == ("AGENT", 95)
419
419
 
420
+ def test_the_collectors_own_wevtutil_polls_are_not_recorded(self):
421
+ """Every poll spawns wevtutil from the panel (as the panel's user, not SYSTEM);
422
+ recording it would add ~8,600 self-referential events a day per VPS."""
423
+ _win(
424
+ _xml(4688, 1, NewProcessId="0x300", ProcessId="0x50", SubjectUserName="Administrator",
425
+ SubjectLogonId="0xabc", NewProcessName="C:\\Windows\\System32\\wevtutil.exe",
426
+ CommandLine="wevtutil qe Security /f:xml"),
427
+ # its console host is a child of the wevtutil, not of the panel
428
+ _xml(4688, 2, NewProcessId="0x301", ProcessId="0x300", SubjectUserName="Administrator",
429
+ SubjectLogonId="0xabc", NewProcessName="C:\\Windows\\System32\\conhost.exe"),
430
+ self_pid=0x50,
431
+ )
432
+ assert _queued() == ([], [])
433
+
434
+ def test_other_children_of_the_panel_are_still_recorded(self):
435
+ """Only the poll tool is filtered — an agent run or git the panel spawns is not."""
436
+ _win(_xml(4688, 1, NewProcessId="0x300", ProcessId="0x50", SubjectUserName="Administrator",
437
+ SubjectLogonId="0xabc", NewProcessName="C:\\Program Files\\Git\\cmd\\git.exe"),
438
+ self_pid=0x50)
439
+ _, events = _queued()
440
+ assert [e["processName"] for e in events] == ["git.exe"]
441
+
442
+ def test_a_wevtutil_someone_else_runs_is_recorded(self):
443
+ """A person auditing the box by hand is activity; only OUR polls are noise."""
444
+ _win(_xml(4688, 1, NewProcessId="0x300", ProcessId="0x77", SubjectUserName="alice",
445
+ SubjectLogonId="0xabc", NewProcessName="C:\\Windows\\System32\\wevtutil.exe"),
446
+ self_pid=0x50)
447
+ _, events = _queued()
448
+ assert [e["processName"] for e in events] == ["wevtutil.exe"]
449
+
420
450
  def test_a_failed_logon_is_a_warning_event(self):
421
451
  _win(_xml(4625, 1, TargetUserName="admin", IpAddress="198.51.100.9"))
422
452
  _, events = _queued()
@@ -134,3 +134,97 @@ class TestInstallation:
134
134
  ok, msg = service.install()
135
135
  assert ok is True
136
136
  assert "panel ok" in msg and "timer broke" in msg
137
+
138
+
139
+ class TestWindowsUpdateTask:
140
+ """The Windows counterpart of the timer. Without it a Windows VPS only ever
141
+ updated when someone ran `agentos-auto update` by hand — so a feature shipped to
142
+ the fleet (the activity audit trail) silently never reached those boxes."""
143
+
144
+ _NS = "{http://schemas.microsoft.com/windows/2004/02/mit/task}"
145
+
146
+ def _xml(self, exe=r"C:\Users\Administrator\agentsystem\venv\Scripts\agentos-auto.exe"):
147
+ import xml.etree.ElementTree as ET
148
+ return ET.fromstring(service._update_task_xml(exe, r"C:\Users\Administrator")
149
+ .split("\n", 1)[1]) # ET rejects the UTF-16 declaration on a str
150
+
151
+ def test_runs_agentos_auto_update(self):
152
+ ex = self._xml().find(f"{self._NS}Actions/{self._NS}Exec")
153
+ assert ex.findtext(f"{self._NS}Command").endswith("agentos-auto.exe")
154
+ assert ex.findtext(f"{self._NS}Arguments") == "update"
155
+ assert ex.findtext(f"{self._NS}WorkingDirectory") == r"C:\Users\Administrator"
156
+
157
+ def test_daily_and_randomized_like_the_timer(self):
158
+ trig = self._xml().find(f"{self._NS}Triggers/{self._NS}CalendarTrigger")
159
+ assert trig.findtext(f"{self._NS}ScheduleByDay/{self._NS}DaysInterval") == "1"
160
+ assert trig.findtext(f"{self._NS}RandomDelay") == f"PT{service.UPDATE_RANDOM_DELAY_S // 60}M"
161
+
162
+ def test_catches_up_after_downtime(self):
163
+ """systemd's Persistent=true: a box that was off at 03:00 updates when next up."""
164
+ assert self._xml().findtext(f"{self._NS}Settings/{self._NS}StartWhenAvailable") == "true"
165
+
166
+ def test_runs_elevated_as_the_user(self):
167
+ """Elevated so the updater's audit step can run auditpol; as the user so
168
+ ~/.agentos (token, .env) is the right profile."""
169
+ pr = self._xml().find(f"{self._NS}Principals/{self._NS}Principal")
170
+ assert pr.findtext(f"{self._NS}RunLevel") == "HighestAvailable"
171
+ assert pr.findtext(f"{self._NS}LogonType") == "InteractiveToken"
172
+
173
+ def test_a_path_with_xml_metacharacters_is_escaped(self):
174
+ exe = r"C:\R&D <x>\agentos-auto.exe"
175
+ assert self._xml(exe).find(
176
+ f"{self._NS}Actions/{self._NS}Exec/{self._NS}Command").text == exe
177
+
178
+ def test_creates_the_task_from_a_utf16_xml_file(self, monkeypatch):
179
+ monkeypatch.setattr(service, "IS_WINDOWS", True)
180
+ seen = {}
181
+
182
+ def fake_run(argv):
183
+ seen["argv"] = argv
184
+ seen["xml"] = Path(argv[argv.index("/XML") + 1]).read_text(encoding="utf-16")
185
+ return 0, "SUCCESS"
186
+ monkeypatch.setattr(service, "_run", fake_run)
187
+
188
+ ok, msg = service._install_update_timer()
189
+ assert ok is True and service.WIN_UPDATE_TASK in msg
190
+ assert seen["argv"][:4] == ["schtasks", "/Create", "/TN", service.WIN_UPDATE_TASK]
191
+ assert seen["argv"][-1] == "/F"
192
+ assert "<Arguments>update</Arguments>" in seen["xml"]
193
+ assert not Path(seen["argv"][seen["argv"].index("/XML") + 1]).exists() # temp file removed
194
+
195
+ def test_a_failed_create_is_reported_not_raised(self, monkeypatch):
196
+ monkeypatch.setattr(service, "IS_WINDOWS", True)
197
+ monkeypatch.setattr(service, "_run", lambda argv: (1, "Access is denied."))
198
+ ok, msg = service._install_update_timer()
199
+ assert ok is False and "Access is denied." in msg
200
+
201
+ def test_detection_queries_the_task(self, monkeypatch):
202
+ monkeypatch.setattr(service, "IS_WINDOWS", True)
203
+ calls = []
204
+ monkeypatch.setattr(service, "_run", lambda argv: (calls.append(argv), (0, ""))[1])
205
+ assert service.update_timer_installed() is True
206
+ assert calls == [["schtasks", "/Query", "/TN", service.WIN_UPDATE_TASK]]
207
+ monkeypatch.setattr(service, "_run", lambda argv: (1, "cannot find"))
208
+ assert service.update_timer_installed() is False
209
+
210
+ def test_install_creates_both_tasks_and_a_timer_failure_is_not_fatal(self, monkeypatch):
211
+ monkeypatch.setattr(service, "IS_WINDOWS", True)
212
+ monkeypatch.setattr(service, "_install_schtask", lambda h, p: (True, "panel ok"))
213
+ monkeypatch.setattr(service, "_install_update_timer", lambda: (False, "timer broke"))
214
+ ok, msg = service.install()
215
+ assert ok is True and "panel ok" in msg and "timer broke" in msg
216
+
217
+ def test_uninstall_removes_the_update_task_too(self, monkeypatch):
218
+ monkeypatch.setattr(service, "IS_WINDOWS", True)
219
+ calls = []
220
+ monkeypatch.setattr(service, "_run", lambda argv: (calls.append(argv), (0, ""))[1])
221
+ service.uninstall()
222
+ assert ["schtasks", "/Delete", "/TN", service.WIN_UPDATE_TASK, "/F"] in calls
223
+ assert ["schtasks", "/Delete", "/TN", service.WIN_TASK, "/F"] in calls
224
+
225
+ def test_resolves_the_exe_console_script_on_windows(self, monkeypatch, tmp_path):
226
+ """A venv's Scripts dir holds agentos-auto.EXE; the bare name never exists there."""
227
+ (tmp_path / "agentos-auto.exe").write_text("")
228
+ monkeypatch.setattr(service, "IS_WINDOWS", True)
229
+ monkeypatch.setattr(service.sys, "executable", str(tmp_path / "python.exe"))
230
+ assert service._auto_exe() == str(tmp_path / "agentos-auto.exe")