agentwire-dev 1.22.0__tar.gz → 1.23.0__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 (342) hide show
  1. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/CHANGELOG.md +16 -0
  2. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/PKG-INFO +1 -1
  3. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/__init__.py +1 -1
  4. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/__main__.py +82 -6
  5. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/scheduler.py +299 -32
  6. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/scheduled-workloads.md +24 -4
  7. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/workflows.md +126 -1
  8. agentwire_dev-1.23.0/tests/integration/test_scheduler_workflow.py +213 -0
  9. agentwire_dev-1.23.0/tests/unit/test_scheduler.py +300 -0
  10. agentwire_dev-1.22.0/tests/unit/test_scheduler.py +0 -147
  11. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/.github/FUNDING.yml +0 -0
  12. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  13. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  14. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/.github/ISSUE_TEMPLATE/question.md +0 -0
  15. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  16. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/.gitignore +0 -0
  17. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/CLA.md +0 -0
  18. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/CODE_OF_CONDUCT.md +0 -0
  19. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/CONTRIBUTING.md +0 -0
  20. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/Dockerfile.local +0 -0
  21. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/Dockerfile.runpod +0 -0
  22. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/LICENSE +0 -0
  23. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/README.md +0 -0
  24. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/RELEASING.md +0 -0
  25. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/SECURITY.md +0 -0
  26. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/SPONSORS.md +0 -0
  27. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/agents/__init__.py +0 -0
  28. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/agents/base.py +0 -0
  29. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/agents/tmux.py +0 -0
  30. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/bridges/__init__.py +0 -0
  31. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/bridges/telegram.py +0 -0
  32. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/cached_status.py +0 -0
  33. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/channels/__init__.py +0 -0
  34. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/channels/_template.py +0 -0
  35. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/channels/base.py +0 -0
  36. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/channels/discord.py +0 -0
  37. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/channels/email.py +0 -0
  38. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/channels/quo.py +0 -0
  39. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/channels/slack.py +0 -0
  40. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/channels/sms.py +0 -0
  41. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/channels/telegram.py +0 -0
  42. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/channels/webhook.py +0 -0
  43. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/cli_safety.py +0 -0
  44. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/completion.py +0 -0
  45. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/config.py +0 -0
  46. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/errors.py +0 -0
  47. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/history.py +0 -0
  48. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/hooks/__init__.py +0 -0
  49. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/hooks/agentwire-permission.sh +0 -0
  50. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/hooks/damage-control/__init__.py +0 -0
  51. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/hooks/damage-control/audit_logger.py +0 -0
  52. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/hooks/damage-control/bash-tool-damage-control.py +0 -0
  53. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/hooks/damage-control/edit-tool-damage-control.py +0 -0
  54. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/hooks/damage-control/rules/agentwire.yaml +0 -0
  55. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/hooks/damage-control/rules/aws.yaml +0 -0
  56. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/hooks/damage-control/rules/cloud-hosting.yaml +0 -0
  57. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/hooks/damage-control/rules/containers.yaml +0 -0
  58. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/hooks/damage-control/rules/core.yaml +0 -0
  59. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/hooks/damage-control/rules/databases.yaml +0 -0
  60. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/hooks/damage-control/rules/firebase.yaml +0 -0
  61. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/hooks/damage-control/rules/gcp.yaml +0 -0
  62. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/hooks/damage-control/rules/git.yaml +0 -0
  63. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/hooks/damage-control/rules/gws.yaml +0 -0
  64. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/hooks/damage-control/rules/infrastructure.yaml +0 -0
  65. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/hooks/damage-control/rules/remote.yaml +0 -0
  66. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/hooks/damage-control/write-tool-damage-control.py +0 -0
  67. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/hooks/idle-handler.sh +0 -0
  68. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/init_agentwire.py +0 -0
  69. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/listen.py +0 -0
  70. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/locking.py +0 -0
  71. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/mcp_server.py +0 -0
  72. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/network.py +0 -0
  73. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/onboarding.py +0 -0
  74. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/overnight.py +0 -0
  75. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/pane_manager.py +0 -0
  76. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/project_config.py +0 -0
  77. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/projects.py +0 -0
  78. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/prompts/__init__.py +0 -0
  79. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/prompts/init.md +0 -0
  80. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/roles/__init__.py +0 -0
  81. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/roles/agentwire.md +0 -0
  82. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/roles/channel-admin.md +0 -0
  83. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/roles/chatbot.md +0 -0
  84. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/roles/discord-dm.md +0 -0
  85. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/roles/init.md +0 -0
  86. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/roles/notifications.md +0 -0
  87. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/roles/orchestrator.md +0 -0
  88. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/roles/slack-dm.md +0 -0
  89. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/roles/task-runner.md +0 -0
  90. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/roles/voice.md +0 -0
  91. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/roles/worker.md +0 -0
  92. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/server.py +0 -0
  93. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/agentwire-Echo--black.png +0 -0
  94. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/agentwire-Echo--transparent.png +0 -0
  95. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/agentwire-Echo.png +0 -0
  96. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/agentwire-email-banner.png +0 -0
  97. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/agentwire-splash-logo-layers--agentwire-text.png +0 -0
  98. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/agentwire-splash-logo-layers--echo-claw-fg.png +0 -0
  99. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/agentwire-splash-logo-layers--echo.png +0 -0
  100. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/agentwire-splash-logo-layers--full--transparent-top.png +0 -0
  101. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/agentwire-splash-logo-layers--full-black.png +0 -0
  102. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/agentwire-splash-logo-layers--telephone-fg.png +0 -0
  103. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/agentwire-splash-logo-layers--telephone.png +0 -0
  104. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/agentwire-splash-logo-layers--transparent-top.png +0 -0
  105. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/agentwire-splash-logo-layers--transparent.png +0 -0
  106. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/agentwire-splash-logo-layers--tree.png +0 -0
  107. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/agentwire-splash-logo-layers.png +0 -0
  108. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/css/desktop.css +0 -0
  109. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/favicon.png +0 -0
  110. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/machines/android.jpeg +0 -0
  111. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/machines/automaton.jpeg +0 -0
  112. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/machines/bot.jpeg +0 -0
  113. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/machines/cyborg.jpeg +0 -0
  114. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/machines/droid.jpeg +0 -0
  115. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/machines/drone.jpeg +0 -0
  116. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/machines/guardian.jpeg +0 -0
  117. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/machines/mech.jpeg +0 -0
  118. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/machines/probe.jpeg +0 -0
  119. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/machines/robot.jpeg +0 -0
  120. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/machines/sentinel.jpeg +0 -0
  121. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/machines/unit.jpeg +0 -0
  122. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/projects/blob.jpeg +0 -0
  123. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/projects/cloud.jpeg +0 -0
  124. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/projects/crystal.jpeg +0 -0
  125. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/projects/cyclops.jpeg +0 -0
  126. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/projects/flame.jpeg +0 -0
  127. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/projects/fuzzy.jpeg +0 -0
  128. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/projects/horned.jpeg +0 -0
  129. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/projects/moon.jpeg +0 -0
  130. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/projects/slime.jpeg +0 -0
  131. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/projects/star.jpeg +0 -0
  132. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/projects/tentacle.jpeg +0 -0
  133. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/projects/winged.jpeg +0 -0
  134. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/sessions/bear.jpeg +0 -0
  135. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/sessions/cat.jpeg +0 -0
  136. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/sessions/crown.jpeg +0 -0
  137. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/sessions/custom/agentwire-portal.png +0 -0
  138. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/sessions/custom/agentwire-tts.png +0 -0
  139. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/sessions/custom/agentwire.png +0 -0
  140. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/sessions/deer.jpeg +0 -0
  141. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/sessions/drone.jpeg +0 -0
  142. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/sessions/eagle.jpeg +0 -0
  143. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/sessions/fox.jpeg +0 -0
  144. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/sessions/hawk.jpeg +0 -0
  145. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/sessions/horse.jpeg +0 -0
  146. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/sessions/lion.jpeg +0 -0
  147. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/sessions/rabbit.jpeg +0 -0
  148. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/sessions/robot.jpeg +0 -0
  149. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/sessions/tiger.jpeg +0 -0
  150. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/icons/sessions/wolf.jpeg +0 -0
  151. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/js/.gitkeep +0 -0
  152. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/js/artifact-window.js +0 -0
  153. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/js/components/icon-picker.js +0 -0
  154. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/js/components/list-card.js +0 -0
  155. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/js/components/type-tag.js +0 -0
  156. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/js/desktop-manager.js +0 -0
  157. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/js/desktop.js +0 -0
  158. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/js/icon-manager.js +0 -0
  159. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/js/notifications-panel.js +0 -0
  160. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/js/session-window.js +0 -0
  161. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/js/sidebar/artifacts-section.js +0 -0
  162. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/js/sidebar/config-section.js +0 -0
  163. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/js/sidebar/machines-section.js +0 -0
  164. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/js/sidebar/projects-section.js +0 -0
  165. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/js/sidebar/scheduler-section.js +0 -0
  166. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/js/sidebar/services-section.js +0 -0
  167. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/js/sidebar/sessions-section.js +0 -0
  168. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/js/sidebar/socials-section.js +0 -0
  169. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/js/sidebar.js +0 -0
  170. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/js/tile-manager.js +0 -0
  171. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/js/utils/auto-refresh.js +0 -0
  172. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/js/winbox.bundle.min.js +0 -0
  173. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/static/js/windows/chat-window.js +0 -0
  174. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/stt/__init__.py +0 -0
  175. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/stt/base.py +0 -0
  176. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/stt/server_backend.py +0 -0
  177. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/stt/stt_server.py +0 -0
  178. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/stt/whisperkit.py +0 -0
  179. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/tasks.py +0 -0
  180. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/templates/__init__.py +0 -0
  181. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/templates/base.html +0 -0
  182. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/templates/desktop.html +0 -0
  183. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/templates/email_notification.html +0 -0
  184. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/templates/tmux.conf +0 -0
  185. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/templating.py +0 -0
  186. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/tooldefs/aws.yaml +0 -0
  187. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/tooldefs/docker.yaml +0 -0
  188. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/tooldefs/gcp.yaml +0 -0
  189. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/tooldefs/gh.yaml +0 -0
  190. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/tooldefs/git.yaml +0 -0
  191. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/tooldefs/gws.yaml +0 -0
  192. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/tooldefs/kubectl.yaml +0 -0
  193. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/tooldefs/npm.yaml +0 -0
  194. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/tooldefs/terraform.yaml +0 -0
  195. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/tooldefs/uv.yaml +0 -0
  196. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/tts/__init__.py +0 -0
  197. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/tts/base.py +0 -0
  198. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/tts/engines/__init__.py +0 -0
  199. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/tts/engines/chatterbox.py +0 -0
  200. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/tts/engines/kokoro.py +0 -0
  201. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/tts/engines/qwen_base.py +0 -0
  202. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/tts/engines/qwen_custom.py +0 -0
  203. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/tts/engines/qwen_design.py +0 -0
  204. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/tts/engines/zonos.py +0 -0
  205. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/tts/registry.py +0 -0
  206. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/tts/runpod_handler.py +0 -0
  207. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/tts_server.py +0 -0
  208. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/tunnels.py +0 -0
  209. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/utils/__init__.py +0 -0
  210. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/utils/chunker.py +0 -0
  211. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/utils/file_io.py +0 -0
  212. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/utils/paths.py +0 -0
  213. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/utils/subprocess.py +0 -0
  214. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/validation.py +0 -0
  215. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/voiceclone.py +0 -0
  216. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/voices/darren.wav +0 -0
  217. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/voices/default.wav +0 -0
  218. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/voices/jessica.wav +0 -0
  219. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/voices/lisa.wav +0 -0
  220. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/voices/may.wav +0 -0
  221. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/workflows/__init__.py +0 -0
  222. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/workflows/cli.py +0 -0
  223. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/workflows/context.py +0 -0
  224. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/workflows/definitions.py +0 -0
  225. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/workflows/node.py +0 -0
  226. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/workflows/outputs.py +0 -0
  227. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/workflows/pi_runner.py +0 -0
  228. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/workflows/runner.py +0 -0
  229. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/workflows/storage.py +0 -0
  230. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/agentwire/worktree.py +0 -0
  231. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/FEATURE-REQUESTS.md +0 -0
  232. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/FR-auto-mode-session-type.md +0 -0
  233. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/PORTAL.md +0 -0
  234. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/SHELL_ESCAPING.md +0 -0
  235. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/SPONSORS.md +0 -0
  236. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/TROUBLESHOOTING.md +0 -0
  237. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/README.md +0 -0
  238. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-agent-hot-swap.md +0 -0
  239. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-ambient-context-stream.md +0 -0
  240. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-ambient-listening-mode.md +0 -0
  241. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-audio-cues.md +0 -0
  242. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-checkpoint-commits.md +0 -0
  243. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-context-compression-protocol.md +0 -0
  244. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-context-window-gauge.md +0 -0
  245. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-contextual-bookmarks.md +0 -0
  246. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-conversation-archaeology.md +0 -0
  247. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-cost-tracking-dashboard.md +0 -0
  248. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-cross-session-events.md +0 -0
  249. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-delegation-replay.md +0 -0
  250. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-device-session-tethering.md +0 -0
  251. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-failure-memory.md +0 -0
  252. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-notification-escalation.md +0 -0
  253. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-periodic-voice-briefings.md +0 -0
  254. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-presence-aware-sessions.md +0 -0
  255. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-session-drift-detection.md +0 -0
  256. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-session-energy-model.md +0 -0
  257. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-session-handshake.md +0 -0
  258. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-session-momentum.md +0 -0
  259. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-session-replay.md +0 -0
  260. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-session-snapshots.md +0 -0
  261. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-session-templates.md +0 -0
  262. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-session-thermal-throttling.md +0 -0
  263. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-smart-session-routing.md +0 -0
  264. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-spatial-voice-mixing.md +0 -0
  265. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-speculative-execution.md +0 -0
  266. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-task-pipeline-chaining.md +0 -0
  267. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-task-pivot-protocol.md +0 -0
  268. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-task-time-budgets.md +0 -0
  269. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-voice-activity-zones.md +0 -0
  270. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-voice-breakpoints.md +0 -0
  271. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-voice-code-review.md +0 -0
  272. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-voice-command-undo.md +0 -0
  273. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-voice-handoff.md +0 -0
  274. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-voice-identity.md +0 -0
  275. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-voice-interrupts-v2.md +0 -0
  276. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-voice-interrupts.md +0 -0
  277. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-voice-macros.md +0 -0
  278. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-voice-transcript-logs.md +0 -0
  279. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-watchdog-mode.md +0 -0
  280. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-worker-fencing.md +0 -0
  281. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-worker-file-coordination.md +0 -0
  282. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-worker-health-dashboard.md +0 -0
  283. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-worker-heartbeat-watchdog.md +0 -0
  284. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-worker-progress-streaming.md +0 -0
  285. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-worker-proof-of-work.md +0 -0
  286. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-worker-warmup.md +0 -0
  287. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/brainstorms/idea-x-api-integration.md +0 -0
  288. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/channels.md +0 -0
  289. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/claude-code-auto-mode.md +0 -0
  290. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/critical-analysis.md +0 -0
  291. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/demo-script.md +0 -0
  292. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/gws-google-workspace-cli.md +0 -0
  293. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/hammerspoon.md +0 -0
  294. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/issues/pending-code-changes.md +0 -0
  295. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/issues/projects-dedup-ignores-machine.md +0 -0
  296. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/issues/remote-tts-session-detection.md +0 -0
  297. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/issues/tmux-config-onboarding.md +0 -0
  298. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/issues/tmux-paste-freeze.md +0 -0
  299. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/issues/tmux-recommended-config.md +0 -0
  300. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/logo.png +0 -0
  301. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/notification-hooks.md +0 -0
  302. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/pi-zai.md +0 -0
  303. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/progressive-loading-pattern.md +0 -0
  304. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/prompts/agentwire-website.md +0 -0
  305. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/releasing.md +0 -0
  306. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/remote-access.md +0 -0
  307. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/remote-machines.md +0 -0
  308. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/runpod-tts.md +0 -0
  309. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/security/damage-control-migration.md +0 -0
  310. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/security/damage-control.md +0 -0
  311. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/tmux-hooks.md +0 -0
  312. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/tts-self-hosted.md +0 -0
  313. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/docs/youtube-channel.md +0 -0
  314. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/pyproject.toml +0 -0
  315. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/requirements-tts.txt +0 -0
  316. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/tests/conftest.py +0 -0
  317. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/tests/e2e/test_portal_ui.py +0 -0
  318. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/tests/fixtures/sample_agentwire.yml +0 -0
  319. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/tests/fixtures/sample_config.yaml +0 -0
  320. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/tests/fixtures/sample_scheduler.yaml +0 -0
  321. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/tests/integration/test_build_agent_command.py +0 -0
  322. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/tests/integration/test_channels_cli.py +0 -0
  323. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/tests/integration/test_cli_commands.py +0 -0
  324. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/tests/integration/test_cli_output.py +0 -0
  325. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/tests/integration/test_mcp_tools.py +0 -0
  326. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/tests/integration/test_portal_api.py +0 -0
  327. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/tests/integration/test_scheduler_board.py +0 -0
  328. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/tests/unit/test_channels.py +0 -0
  329. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/tests/unit/test_cli_safety.py +0 -0
  330. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/tests/unit/test_config.py +0 -0
  331. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/tests/unit/test_file_io.py +0 -0
  332. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/tests/unit/test_history.py +0 -0
  333. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/tests/unit/test_locking.py +0 -0
  334. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/tests/unit/test_mcp_server.py +0 -0
  335. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/tests/unit/test_project_config.py +0 -0
  336. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/tests/unit/test_roles.py +0 -0
  337. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/tests/unit/test_server_async.py +0 -0
  338. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/tests/unit/test_server_pure.py +0 -0
  339. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/tests/unit/test_tasks.py +0 -0
  340. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/tests/unit/test_templating.py +0 -0
  341. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/tests/unit/test_workflows.py +0 -0
  342. {agentwire_dev-1.22.0 → agentwire_dev-1.23.0}/tests/unit/test_worktree.py +0 -0
@@ -7,8 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.23.0] - 2026-04-16
11
+
10
12
  ### Added
11
13
 
14
+ - **Workflow-backed scheduler tasks** — scheduler can now dispatch a pi workflow DAG in-process instead of shelling out to `agentwire ensure` (Phase 3 of the pi workflow roadmap)
15
+ - New `workflow:` + `inputs:` fields on scheduler tasks in `~/.agentwire/scheduler.yaml` (mutually exclusive with `task:`)
16
+ - `dispatch_task()` routes automatically: ensure path unchanged, new `_dispatch_workflow_task` calls `run_workflow()` in-process — no tmux, no Claude Code subprocess
17
+ - Status mapping: workflow `success→complete`, `partial→incomplete`, `failure→failed`
18
+ - Scheduler `{{ task }}`, `{{ project }}`, `{{ session }}`, `{{ workflow }}` variables expand in string `inputs:` values
19
+ - `agentwire scheduler run <name> --dry-run` prints the workflow plan without touching state
20
+ - `task_completed` events now carry `workflow`, `run_id`, and per-node `nodes[]` when the task is workflow-backed
21
+ - Morning report (`agentwire scheduler report --artifact`) renders per-node status badges + run-id breadcrumb for workflow rows
22
+ - `agentwire scheduler history --json` includes the `workflow` name per task
23
+ - Full reference: `docs/workflows.md` → "Scheduler integration"; compare/contrast: `docs/scheduled-workloads.md`
12
24
  - **Kokoro TTS engine** — CPU-only ultra-lightweight backend (`kokoro`)
13
25
  - Kokoro 82M ONNX model via `kokoro-onnx`, auto-downloads ~170 MB from HuggingFace on first use
14
26
  - No GPU required — pure ONNX CPU inference, near real-time on Apple Silicon / modern Intel CPU
@@ -16,6 +28,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
16
28
  - Streaming support via `create_stream()`
17
29
  - Runs in dedicated `.venv-kokoro` with CPU-only PyTorch (~250 MB vs 2 GB+ CUDA builds)
18
30
 
31
+ ### Fixed
32
+
33
+ - `cmd_scheduler_report` was calling `read_events(limit=500)` with the wrong kwarg name; the `except` silently caught the `TypeError` so morning reports quietly returned 0 events. Now calls `read_events(tail=500)`.
34
+
19
35
  ## [1.9.0] - 2026-03-13
20
36
 
21
37
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentwire-dev
3
- Version: 1.22.0
3
+ Version: 1.23.0
4
4
  Summary: Multi-session voice web interface for AI coding agents
5
5
  Project-URL: Homepage, https://agentwire.dev
6
6
  Project-URL: Repository, https://github.com/dotdevdotdev/agentwire-dev
@@ -1,3 +1,3 @@
1
1
  """AgentWire - Multi-session voice web interface for AI coding agents."""
2
2
 
3
- __version__ = "1.22.0"
3
+ __version__ = "1.23.0"
@@ -8744,9 +8744,10 @@ def cmd_scheduler_board(args) -> int:
8744
8744
 
8745
8745
  def cmd_scheduler_run(args) -> int:
8746
8746
  """Force-run a specific task now."""
8747
- from .scheduler import dispatch_task, load_board, save_board
8747
+ from .scheduler import _render_workflow_inputs, dispatch_task, load_board, save_board
8748
8748
 
8749
8749
  json_mode = getattr(args, 'json', False)
8750
+ dry_run = getattr(args, 'dry_run', False)
8750
8751
  name = args.name
8751
8752
 
8752
8753
  try:
@@ -8760,6 +8761,40 @@ def cmd_scheduler_run(args) -> int:
8760
8761
  f"Task '{name}' not found in board. Available: {', '.join(board.tasks.keys())}",
8761
8762
  )
8762
8763
 
8764
+ task = board.tasks[name]
8765
+
8766
+ if dry_run:
8767
+ if not task.workflow:
8768
+ return _output_result(
8769
+ False, json_mode,
8770
+ f"--dry-run only applies to workflow tasks; '{name}' runs via ensure.",
8771
+ )
8772
+ from agentwire.workflows.definitions import resolve_workflow
8773
+ from agentwire.workflows.runner import run_workflow
8774
+ try:
8775
+ wf = resolve_workflow(task.workflow)
8776
+ except Exception as e:
8777
+ return _output_result(False, json_mode, f"Could not load workflow '{task.workflow}': {e}")
8778
+ rendered_inputs = _render_workflow_inputs(task.inputs, task)
8779
+ run = run_workflow(wf, runs_dir=None, inputs=rendered_inputs, dry_run=True)
8780
+ if json_mode:
8781
+ _output_json({
8782
+ "success": True,
8783
+ "task": name,
8784
+ "dry_run": True,
8785
+ "workflow": wf.name,
8786
+ "inputs": rendered_inputs,
8787
+ "nodes": [r.node_id for r in run.node_results],
8788
+ "status": run.status,
8789
+ })
8790
+ return 0
8791
+ print(f"Dry-run: {name} → workflow '{wf.name}'")
8792
+ if rendered_inputs:
8793
+ print(f" inputs: {rendered_inputs}")
8794
+ for r in run.node_results:
8795
+ print(f" - {r.node_id}: {r.final_text}")
8796
+ return 0
8797
+
8763
8798
  if not json_mode:
8764
8799
  print(f"Running: {name}")
8765
8800
 
@@ -8835,12 +8870,14 @@ def cmd_scheduler_history(args) -> int:
8835
8870
  if json_mode:
8836
8871
  history = []
8837
8872
  for name, state in board.state.items():
8873
+ task = board.tasks.get(name)
8838
8874
  history.append({
8839
8875
  "task": name,
8840
8876
  "last_run": state.last_run.isoformat() if state.last_run else None,
8841
8877
  "last_status": state.last_status,
8842
8878
  "last_duration": state.last_duration,
8843
8879
  "run_count": state.run_count,
8880
+ "workflow": (task.workflow if task else "") or None,
8844
8881
  })
8845
8882
  _output_json({"success": True, "history": history})
8846
8883
  return 0
@@ -8867,6 +8904,7 @@ def cmd_scheduler_history(args) -> int:
8867
8904
  def cmd_scheduler_report(args) -> int:
8868
8905
  """Generate a morning report HTML artifact of recent task runs."""
8869
8906
  import re as _re
8907
+ from html import escape as html_escape
8870
8908
  from .scheduler import _parse_duration, format_interval, load_board, read_events
8871
8909
 
8872
8910
  json_mode = getattr(args, 'json', False)
@@ -8886,7 +8924,7 @@ def cmd_scheduler_report(args) -> int:
8886
8924
 
8887
8925
  # Load events in the window
8888
8926
  try:
8889
- events = read_events(limit=500)
8927
+ events = read_events(tail=500)
8890
8928
  except Exception:
8891
8929
  events = []
8892
8930
 
@@ -8912,6 +8950,9 @@ def cmd_scheduler_report(args) -> int:
8912
8950
  "timestamp": ts.strftime("%Y-%m-%d %H:%M"),
8913
8951
  "work_branch": "",
8914
8952
  "pr_url": "",
8953
+ "workflow": ev.get("workflow", ""),
8954
+ "run_id": ev.get("run_id", ""),
8955
+ "nodes": ev.get("nodes") or [],
8915
8956
  }
8916
8957
  runs.append(run)
8917
8958
 
@@ -8935,20 +8976,48 @@ def cmd_scheduler_report(args) -> int:
8935
8976
  color = colors.get(status, "#78909c")
8936
8977
  return f'<span style="background:{color};color:#fff;padding:2px 8px;border-radius:12px;font-size:0.85em">{status}</span>'
8937
8978
 
8979
+ def node_badges(nodes: list) -> str:
8980
+ if not nodes:
8981
+ return ""
8982
+ node_colors = {
8983
+ "success": "#00c853",
8984
+ "failure": "#ff5252",
8985
+ "timeout": "#ff7043",
8986
+ "skipped": "#78909c",
8987
+ }
8988
+ pieces = []
8989
+ for n in nodes:
8990
+ nid = html_escape(str(n.get("id", "?")))
8991
+ nstatus = str(n.get("status", "?"))
8992
+ color = node_colors.get(nstatus, "#556")
8993
+ pieces.append(
8994
+ f'<span style="background:{color};color:#fff;padding:1px 6px;border-radius:8px;font-size:0.75em;margin-right:3px">{nid}</span>'
8995
+ )
8996
+ return "".join(pieces)
8997
+
8938
8998
  rows_html = ""
8939
8999
  for r in runs:
8940
9000
  duration_str = format_interval(r["duration"]) if r["duration"] else "-"
8941
9001
  pr_link = f'<a href="{r["pr_url"]}" target="_blank" style="color:#00d4ff">{r["pr_url"][:40]}...</a>' if r.get("pr_url") else "-"
8942
- branch_str = r.get("work_branch") or "-"
9002
+ branch_col = ""
9003
+ if r.get("workflow"):
9004
+ wf_label = html_escape(r["workflow"])
9005
+ run_id = html_escape(r.get("run_id", ""))
9006
+ badges = node_badges(r["nodes"])
9007
+ run_hint = f' <span style="color:#556;font-size:0.75em">({run_id[:20]}…)</span>' if run_id else ""
9008
+ branch_col = f'<code style="font-size:0.85em">workflow:{wf_label}</code>{run_hint}<div style="margin-top:4px">{badges}</div>'
9009
+ else:
9010
+ branch_col = f'<code style="font-size:0.85em">{r.get("work_branch") or "-"}</code>'
9011
+ summary_text = r["summary"][:120] if r["summary"] else "-"
8943
9012
  rows_html += f"""
8944
9013
  <tr>
8945
9014
  <td style="font-weight:600">{r["task"]}</td>
8946
9015
  <td>{status_badge(r["status"])}</td>
8947
9016
  <td>{r["timestamp"]}</td>
8948
9017
  <td>{duration_str}</td>
8949
- <td><code style="font-size:0.85em">{branch_str}</code></td>
9018
+ <td>{branch_col}</td>
8950
9019
  <td>{pr_link}</td>
8951
- <td style="color:#aaa;font-size:0.85em">{r["summary"][:120] if r["summary"] else "-"}</td>
9020
+ <td style="color:#aaa;font-size:0.85em">{summary_text}</td>
8952
9021
  </tr>"""
8953
9022
 
8954
9023
  if not rows_html:
@@ -10550,7 +10619,12 @@ def main() -> int:
10550
10619
  scheduler_parser = subparsers.add_parser(
10551
10620
  "scheduler",
10552
10621
  help="Manage the task scheduler",
10553
- description="Centralized daemon that dispatches tasks across projects on a shared cadence.",
10622
+ description=(
10623
+ "Centralized daemon that dispatches tasks across projects on a shared cadence. "
10624
+ "Tasks in ~/.agentwire/scheduler.yaml are either ensure tasks (task: + session:) "
10625
+ "or workflow tasks (workflow: + inputs:) — the scheduler routes each automatically. "
10626
+ "See docs/scheduled-workloads.md."
10627
+ ),
10554
10628
  )
10555
10629
  scheduler_subparsers = scheduler_parser.add_subparsers(dest="scheduler_command")
10556
10630
 
@@ -10580,6 +10654,8 @@ def main() -> int:
10580
10654
  sched_run = scheduler_subparsers.add_parser("run", help="Force-run a task now")
10581
10655
  sched_run.add_argument("name", help="Task name from board")
10582
10656
  sched_run.add_argument("--json", action="store_true", help="Output JSON")
10657
+ sched_run.add_argument("--dry-run", action="store_true",
10658
+ help="For workflow tasks: print the execution plan without running")
10583
10659
  sched_run.set_defaults(func=cmd_scheduler_run)
10584
10660
 
10585
10661
  # scheduler enable <name>
@@ -74,16 +74,18 @@ class Schedule:
74
74
  @dataclass
75
75
  class SchedulerTask:
76
76
  name: str
77
- project: str # ~/projects/foo (expanded at load time)
78
- session: str # session name for ensure
79
- task: str # task name in project's .agentwire.yml
80
- schedule: Schedule # REQUIRED (replaces interval)
77
+ project: str = "" # ~/projects/foo (expanded at load time) — optional for workflow tasks
78
+ session: str = "" # session name for ensure (required iff task is set)
79
+ task: str = "" # task name in project's .agentwire.yml (mutually exclusive with workflow)
80
+ workflow: str = "" # workflow name or YAML path (mutually exclusive with task)
81
+ inputs: dict = field(default_factory=dict) # workflow inputs passed to run_workflow
82
+ schedule: Schedule = field(default_factory=Schedule) # REQUIRED (replaces interval)
81
83
  enabled: bool = True
82
84
  filler: bool = False # only runs in spare cycles
83
85
  priority: int = 99 # task ordering (lower = higher priority)
84
- type: str | None = None # session type override (e.g., claude-bypass)
85
- roles: list[str] | None = None # role override (e.g., ["task-runner"])
86
- model: str | None = None # model override (e.g., "haiku")
86
+ type: str | None = None # session type override (e.g., claude-bypass) — ignored for workflow tasks
87
+ roles: list[str] | None = None # role override ignored for workflow tasks
88
+ model: str | None = None # model override ignored for workflow tasks
87
89
  gate: dict | None = None # precondition gate (git_commit, git_diff, command)
88
90
  max_runs: int | None = None # auto-disable after N successful dispatches
89
91
  once: bool = False # shorthand for max_runs: 1
@@ -216,11 +218,28 @@ def load_board() -> Board:
216
218
 
217
219
  schedule = _parse_schedule(t.get("schedule"))
218
220
 
221
+ raw_project = t.get("project", "")
222
+ project_path = str(Path(raw_project).expanduser()) if raw_project else ""
223
+
224
+ workflow = str(t.get("workflow") or "")
225
+ raw_inputs = t.get("inputs") or {}
226
+ if not isinstance(raw_inputs, dict):
227
+ raw_inputs = {}
228
+
229
+ if workflow:
230
+ session_name = str(t.get("session") or "")
231
+ task_name = str(t.get("task") or "")
232
+ else:
233
+ session_name = str(t.get("session") or name)
234
+ task_name = str(t.get("task") or name)
235
+
219
236
  board.tasks[name] = SchedulerTask(
220
237
  name=name,
221
- project=str(Path(t.get("project", "")).expanduser()),
222
- session=t.get("session", name),
223
- task=t.get("task", name),
238
+ project=project_path,
239
+ session=session_name,
240
+ task=task_name,
241
+ workflow=workflow,
242
+ inputs=dict(raw_inputs),
224
243
  schedule=schedule,
225
244
  enabled=bool(t.get("enabled", True)),
226
245
  filler=bool(t.get("filler", False)),
@@ -986,7 +1005,7 @@ def _auto_commit(task: SchedulerTask, task_name: str, status: str) -> None:
986
1005
 
987
1006
 
988
1007
  def dispatch_task(board: Board, task_name: str) -> TaskState:
989
- """Run a task via `agentwire ensure` and return updated state.
1008
+ """Run a task (workflow DAG or `agentwire ensure`) and return updated state.
990
1009
 
991
1010
  Args:
992
1011
  board: Current board (for reading task config).
@@ -998,16 +1017,8 @@ def dispatch_task(board: Board, task_name: str) -> TaskState:
998
1017
  task = board.tasks[task_name]
999
1018
  existing_state = board.state.get(task_name, TaskState())
1000
1019
 
1001
- # Clear from gated set so gate can be re-evaluated after this run
1002
1020
  _gated_tasks.discard(task_name)
1003
1021
 
1004
- # Clean stale lock for this session before dispatching.
1005
- # Stale locks (from crashed ensure processes) cause --skip-if-locked
1006
- # to silently exit 0, making tasks appear to complete instantly.
1007
- from .locking import remove_stale_lock
1008
- remove_stale_lock(task.session)
1009
-
1010
- # Set last_dispatch BEFORE running for restart safety
1011
1022
  existing_state.last_dispatch = datetime.now(timezone.utc)
1012
1023
  board.state[task_name] = existing_state
1013
1024
  save_board(board)
@@ -1015,6 +1026,21 @@ def dispatch_task(board: Board, task_name: str) -> TaskState:
1015
1026
  _log_event("task_started", task=task_name, session=task.session,
1016
1027
  project=task.project, attempt=existing_state.run_count + 1)
1017
1028
 
1029
+ if task.workflow:
1030
+ return _dispatch_workflow_task(board, task, existing_state)
1031
+ return _dispatch_ensure_task(board, task, existing_state)
1032
+
1033
+
1034
+ def _dispatch_ensure_task(board: Board, task: SchedulerTask, existing_state: TaskState) -> TaskState:
1035
+ """Dispatch via `agentwire ensure` subprocess (tmux session path)."""
1036
+ task_name = task.name
1037
+
1038
+ # Clean stale lock for this session before dispatching.
1039
+ # Stale locks (from crashed ensure processes) cause --skip-if-locked
1040
+ # to silently exit 0, making tasks appear to complete instantly.
1041
+ from .locking import remove_stale_lock
1042
+ remove_stale_lock(task.session)
1043
+
1018
1044
  has_overrides = bool(task.type or task.roles is not None or task.model)
1019
1045
 
1020
1046
  if has_overrides:
@@ -1065,31 +1091,232 @@ def dispatch_task(board: Board, task_name: str) -> TaskState:
1065
1091
  run_count=existing_state.run_count,
1066
1092
  )
1067
1093
 
1068
- # Parse summary from ensure output
1069
1094
  summary, files_modified, blockers_list = _parse_ensure_summary(task, result)
1070
1095
 
1071
- # Log completion event
1072
1096
  _log_event("task_completed", task=task_name, session=task.session,
1073
1097
  status=status, duration=duration, summary=summary,
1074
1098
  files_modified=files_modified, blockers=blockers_list)
1075
1099
 
1076
- # Notify portal
1077
1100
  _notify_portal(task_name, status, duration, summary)
1078
1101
 
1079
- # Auto-commit any changes the task made (each task = one revertable commit)
1080
1102
  _auto_commit(task, task_name, status)
1081
1103
 
1082
- # Capture HEAD for gate checks on next run
1083
- gate_commit = ""
1104
+ gate_commit = _capture_head(task.project)
1105
+
1106
+ new_run_count = existing_state.run_count + 1
1107
+ new_state = TaskState(
1108
+ last_run=datetime.now(timezone.utc),
1109
+ last_status=status,
1110
+ last_duration=duration,
1111
+ run_count=new_run_count,
1112
+ last_summary=summary,
1113
+ last_gate_commit=gate_commit,
1114
+ )
1115
+
1116
+ if task.max_runs is not None and new_run_count >= task.max_runs:
1117
+ task.enabled = False
1118
+ board.state[task_name] = new_state
1119
+ save_board(board)
1120
+ _log_event("task_disabled", task=task_name, reason="max_runs_reached",
1121
+ run_count=new_run_count, max_runs=task.max_runs)
1122
+
1123
+ return new_state
1124
+
1125
+
1126
+ def _capture_head(project: str) -> str:
1127
+ """Return the current HEAD SHA of a git repo, or '' if unavailable."""
1128
+ if not project:
1129
+ return ""
1084
1130
  try:
1085
- head_result = subprocess.run(
1086
- ["git", "-C", task.project, "rev-parse", "HEAD"],
1131
+ result = subprocess.run(
1132
+ ["git", "-C", project, "rev-parse", "HEAD"],
1087
1133
  capture_output=True, text=True, timeout=_sched_config().git_timeout,
1088
1134
  )
1089
- if head_result.returncode == 0:
1090
- gate_commit = head_result.stdout.strip()
1135
+ if result.returncode == 0:
1136
+ return result.stdout.strip()
1091
1137
  except Exception:
1092
1138
  pass
1139
+ return ""
1140
+
1141
+
1142
+ # Workflow dispatch ---------------------------------------------------------
1143
+
1144
+ _WORKFLOW_STATUS_TO_SCHED = {
1145
+ "success": "complete",
1146
+ "partial": "incomplete",
1147
+ "failure": "failed",
1148
+ }
1149
+
1150
+ _SCHED_INPUT_VAR_RE = re.compile(r"\{\{\s*(\w+)\s*\}\}")
1151
+
1152
+
1153
+ def _render_workflow_inputs(inputs: dict, task: SchedulerTask) -> dict:
1154
+ """Expand `{{ task }}`, `{{ project }}`, `{{ session }}`, `{{ workflow }}` in string inputs.
1155
+
1156
+ Unknown variables pass through untouched so workflow nodes can reference their
1157
+ own Jinja variables without collision.
1158
+ """
1159
+ vars_ = {
1160
+ "task": task.name,
1161
+ "project": task.project,
1162
+ "session": task.session,
1163
+ "workflow": task.workflow,
1164
+ }
1165
+
1166
+ def _sub(value):
1167
+ if not isinstance(value, str):
1168
+ return value
1169
+ return _SCHED_INPUT_VAR_RE.sub(
1170
+ lambda m: vars_[m.group(1)] if m.group(1) in vars_ else m.group(0),
1171
+ value,
1172
+ )
1173
+
1174
+ return {k: _sub(v) for k, v in (inputs or {}).items()}
1175
+
1176
+
1177
+ def _parse_workflow_summary(run) -> tuple[str, list[str], list[str]]:
1178
+ """Build a single-line summary + rough blockers list from a workflow run."""
1179
+ node_line = ", ".join(f"{r.node_id}={r.status}" for r in run.node_results) or "(no nodes)"
1180
+ head = f"{run.workflow} → {run.status}"
1181
+ last_text = ""
1182
+ for r in reversed(run.node_results):
1183
+ if r.final_text:
1184
+ first_line = r.final_text.strip().splitlines()[0] if r.final_text.strip() else ""
1185
+ last_text = first_line[:200]
1186
+ break
1187
+ summary = f"{head}: {node_line}" + (f" — {last_text}" if last_text else "")
1188
+ blockers: list[str] = []
1189
+ if run.error:
1190
+ blockers.append(run.error)
1191
+ for r in run.node_results:
1192
+ if r.status in ("failure", "timeout") and r.error:
1193
+ blockers.append(f"[{r.node_id}] {r.error}")
1194
+ return summary[:500], [], blockers
1195
+
1196
+
1197
+ def _node_summary_records(run) -> list[dict]:
1198
+ """Project workflow node results into compact dicts for event logs."""
1199
+ return [
1200
+ {
1201
+ "id": r.node_id,
1202
+ "status": r.status,
1203
+ "duration_ms": r.duration_ms,
1204
+ "attempts": r.attempts,
1205
+ "error": r.error,
1206
+ }
1207
+ for r in run.node_results
1208
+ ]
1209
+
1210
+
1211
+ def _workflow_failure_state(
1212
+ board: Board,
1213
+ task: SchedulerTask,
1214
+ existing_state: TaskState,
1215
+ duration: int,
1216
+ summary: str,
1217
+ error: str,
1218
+ workflow_name: str = "",
1219
+ run_id: str = "",
1220
+ ) -> TaskState:
1221
+ """Persist a failure outcome when the workflow couldn't be loaded or never started."""
1222
+ status = "failed"
1223
+ _log_event(
1224
+ "task_completed",
1225
+ task=task.name,
1226
+ session=task.session or "",
1227
+ status=status,
1228
+ duration=duration,
1229
+ summary=summary,
1230
+ files_modified=[],
1231
+ blockers=[error] if error else [],
1232
+ workflow=workflow_name or task.workflow,
1233
+ run_id=run_id,
1234
+ nodes=[],
1235
+ )
1236
+ _notify_portal(task.name, status, duration, summary)
1237
+
1238
+ new_run_count = existing_state.run_count + 1
1239
+ new_state = TaskState(
1240
+ last_run=datetime.now(timezone.utc),
1241
+ last_status=status,
1242
+ last_duration=duration,
1243
+ run_count=new_run_count,
1244
+ last_summary=summary,
1245
+ last_gate_commit=_capture_head(task.project),
1246
+ )
1247
+
1248
+ if task.max_runs is not None and new_run_count >= task.max_runs:
1249
+ task.enabled = False
1250
+ board.state[task.name] = new_state
1251
+ save_board(board)
1252
+ _log_event("task_disabled", task=task.name, reason="max_runs_reached",
1253
+ run_count=new_run_count, max_runs=task.max_runs)
1254
+
1255
+ return new_state
1256
+
1257
+
1258
+ def _dispatch_workflow_task(board: Board, task: SchedulerTask, existing_state: TaskState) -> TaskState:
1259
+ """Dispatch a workflow task in-process via agentwire.workflows.runner.run_workflow."""
1260
+ from agentwire.workflows.cli import RUNS_DIR
1261
+ from agentwire.workflows.definitions import resolve_workflow
1262
+ from agentwire.workflows.runner import run_workflow
1263
+
1264
+ start_time = time.time()
1265
+
1266
+ try:
1267
+ wf = resolve_workflow(task.workflow)
1268
+ except FileNotFoundError:
1269
+ duration = int(time.time() - start_time)
1270
+ return _workflow_failure_state(
1271
+ board, task, existing_state, duration,
1272
+ summary=f"{task.workflow} → failed: workflow not found",
1273
+ error=f"workflow '{task.workflow}' not found",
1274
+ )
1275
+ except Exception as e:
1276
+ duration = int(time.time() - start_time)
1277
+ return _workflow_failure_state(
1278
+ board, task, existing_state, duration,
1279
+ summary=f"{task.workflow} → failed: {e}",
1280
+ error=f"workflow load error: {e}",
1281
+ )
1282
+
1283
+ rendered_inputs = _render_workflow_inputs(task.inputs, task)
1284
+
1285
+ try:
1286
+ run = run_workflow(wf, runs_dir=RUNS_DIR, inputs=rendered_inputs, dry_run=False)
1287
+ except Exception as e:
1288
+ duration = int(time.time() - start_time)
1289
+ return _workflow_failure_state(
1290
+ board, task, existing_state, duration,
1291
+ summary=f"{task.workflow} → failed: runner crashed",
1292
+ error=f"runner crashed: {e}",
1293
+ workflow_name=wf.name,
1294
+ )
1295
+
1296
+ duration = int(time.time() - start_time)
1297
+ status = _WORKFLOW_STATUS_TO_SCHED.get(run.status, "failed")
1298
+ summary, files_modified, blockers = _parse_workflow_summary(run)
1299
+
1300
+ _log_event(
1301
+ "task_completed",
1302
+ task=task.name,
1303
+ session=task.session or "",
1304
+ status=status,
1305
+ duration=duration,
1306
+ summary=summary,
1307
+ files_modified=files_modified,
1308
+ blockers=blockers,
1309
+ workflow=wf.name,
1310
+ run_id=run.run_id,
1311
+ nodes=_node_summary_records(run),
1312
+ )
1313
+
1314
+ _notify_portal(task.name, status, duration, summary)
1315
+
1316
+ if task.project:
1317
+ _auto_commit(task, task.name, status)
1318
+
1319
+ gate_commit = _capture_head(task.project)
1093
1320
 
1094
1321
  new_run_count = existing_state.run_count + 1
1095
1322
  new_state = TaskState(
@@ -1101,12 +1328,11 @@ def dispatch_task(board: Board, task_name: str) -> TaskState:
1101
1328
  last_gate_commit=gate_commit,
1102
1329
  )
1103
1330
 
1104
- # Auto-disable if max_runs reached
1105
1331
  if task.max_runs is not None and new_run_count >= task.max_runs:
1106
1332
  task.enabled = False
1107
- board.state[task_name] = new_state
1333
+ board.state[task.name] = new_state
1108
1334
  save_board(board)
1109
- _log_event("task_disabled", task=task_name, reason="max_runs_reached",
1335
+ _log_event("task_disabled", task=task.name, reason="max_runs_reached",
1110
1336
  run_count=new_run_count, max_runs=task.max_runs)
1111
1337
 
1112
1338
  return new_state
@@ -1398,6 +1624,45 @@ def read_live_state() -> dict | None:
1398
1624
  return None
1399
1625
 
1400
1626
 
1627
+ def _validate_task_payload(name: str, task: SchedulerTask) -> list[str]:
1628
+ """Validate the task vs workflow payload shape for a single task."""
1629
+ errors: list[str] = []
1630
+
1631
+ has_workflow = bool(task.workflow)
1632
+ has_task = bool(task.task)
1633
+
1634
+ if has_workflow and has_task:
1635
+ errors.append(f"{name}: cannot set both 'task' and 'workflow'")
1636
+ if not has_workflow and not has_task:
1637
+ errors.append(f"{name}: must set either 'task' or 'workflow'")
1638
+
1639
+ if task.inputs and not has_workflow:
1640
+ errors.append(f"{name}: 'inputs' only valid with 'workflow'")
1641
+
1642
+ if has_workflow:
1643
+ try:
1644
+ from agentwire.workflows.definitions import resolve_workflow
1645
+ wf = resolve_workflow(task.workflow)
1646
+ except FileNotFoundError:
1647
+ errors.append(f"{name}: workflow '{task.workflow}' not found")
1648
+ return errors
1649
+ except Exception as e:
1650
+ errors.append(f"{name}: workflow '{task.workflow}' could not be loaded: {e}")
1651
+ return errors
1652
+ wf_errors = wf.validate()
1653
+ for err in wf_errors:
1654
+ errors.append(f"{name}: workflow: {err}")
1655
+
1656
+ gate = task.gate or {}
1657
+ if isinstance(gate, dict):
1658
+ needs_project = bool(gate.get("git_commit") or gate.get("git_diff"))
1659
+ if needs_project and not task.project:
1660
+ gate_type = "git_commit" if gate.get("git_commit") else "git_diff"
1661
+ errors.append(f"{name}: gate {gate_type} requires 'project' path")
1662
+
1663
+ return errors
1664
+
1665
+
1401
1666
  def validate_board(board: Board) -> list[str]:
1402
1667
  """Validate board configuration for errors.
1403
1668
 
@@ -1426,6 +1691,8 @@ def validate_board(board: Board) -> list[str]:
1426
1691
  if sched.after and sched.after in board.tasks and not board.tasks[sched.after].enabled:
1427
1692
  errors.append(f"{name}: warning: dependency '{sched.after}' is disabled")
1428
1693
 
1694
+ errors.extend(_validate_task_payload(name, task))
1695
+
1429
1696
  # Circular dependency detection via DFS
1430
1697
  def _has_cycle(start: str, visited: set, path: set) -> bool:
1431
1698
  if start in path:
@@ -8,12 +8,32 @@ Reliable headless task execution for overnight and automated agent workflows.
8
8
 
9
9
  ## Overview
10
10
 
11
- Two primitives work together:
11
+ Three paths for running scheduled work, picked per task:
12
12
 
13
- 1. **`agentwire ensure`** — Reliable session management + task execution with lifecycle hooks
14
- 2. **Tasks in `.agentwire.yml`** Named workflows with pre/prompt/post phases and branch management
13
+ 1. **`agentwire ensure` tasks** — Reliable session management + task execution with lifecycle hooks. A full Claude Code session runs a single prompt from `.agentwire.yml`. Best for multi-step agent work that needs its own branch / PR / MCP tools.
14
+ 2. **Pi workflow tasks** — A YAML DAG of `pi -p --mode json` invocations runs in-process. No tmux session, no project required. Best for deterministic pipelines where each step has clear inputs/outputs and you want cheap per-node retries.
15
+ 3. **Tasks in `.agentwire.yml`** — Named tasks with pre/prompt/post phases and branch management — the substrate for path #1.
15
16
 
16
- For scheduled execution, combine with `~/.agentwire/scheduler.yaml` and the AgentWire scheduler daemon.
17
+ All three are orchestrated by `~/.agentwire/scheduler.yaml` and the AgentWire scheduler daemon. A single board can mix ensure tasks and workflow tasks freely.
18
+
19
+ ---
20
+
21
+ ## Choosing ensure vs workflow
22
+
23
+ | | ensure task | workflow task |
24
+ |---|---|---|
25
+ | Schedule field | `task: <name>` + `session:` | `workflow: <name-or-path>` + `inputs:` |
26
+ | Dispatch | `agentwire ensure` subprocess → tmux session → Claude Code | `run_workflow()` in-process → pi subprocesses per node |
27
+ | Model family | Claude (subscription via session type) | Z.AI glm-5 / flash-tier pi (per-node choice) |
28
+ | Session needed | yes | no |
29
+ | Project needed | yes (for branch mgmt) | optional (only for git gates / auto-commit) |
30
+ | Multi-step logic | inside one Claude prompt | first-class DAG with retries, branches, outputs |
31
+ | Dry-run | no | `scheduler run <name> --dry-run` prints the plan |
32
+ | Cost profile | subscription-covered | Z.AI credits (or free flash tier) |
33
+
34
+ A task must set exactly one of `task:` or `workflow:`. Everything else (gates, priority, `max_runs`, `once`, `cooldown`, `not_before`/`not_after`) applies to both.
35
+
36
+ Full workflow task reference: [`docs/workflows.md#scheduler-integration`](workflows.md#scheduler-integration).
17
37
 
18
38
  ---
19
39