agentwire-dev 1.21.0__tar.gz → 1.22.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 (341) hide show
  1. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/PKG-INFO +1 -1
  2. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/__init__.py +1 -1
  3. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/__main__.py +256 -9
  4. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/config.py +2 -0
  5. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/mcp_server.py +135 -0
  6. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/overnight.py +4 -1
  7. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/project_config.py +6 -2
  8. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/projects.py +87 -0
  9. agentwire_dev-1.22.0/agentwire/roles/notifications.md +83 -0
  10. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/server.py +174 -0
  11. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/css/desktop.css +105 -0
  12. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/js/desktop-manager.js +9 -0
  13. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/js/desktop.js +9 -0
  14. agentwire_dev-1.22.0/agentwire/static/js/notifications-panel.js +138 -0
  15. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/js/sidebar/sessions-section.js +4 -1
  16. agentwire_dev-1.22.0/agentwire/workflows/__init__.py +28 -0
  17. agentwire_dev-1.22.0/agentwire/workflows/cli.py +328 -0
  18. agentwire_dev-1.22.0/agentwire/workflows/context.py +52 -0
  19. agentwire_dev-1.22.0/agentwire/workflows/definitions.py +331 -0
  20. agentwire_dev-1.22.0/agentwire/workflows/node.py +115 -0
  21. agentwire_dev-1.22.0/agentwire/workflows/outputs.py +149 -0
  22. agentwire_dev-1.22.0/agentwire/workflows/pi_runner.py +238 -0
  23. agentwire_dev-1.22.0/agentwire/workflows/runner.py +364 -0
  24. agentwire_dev-1.22.0/agentwire/workflows/storage.py +186 -0
  25. agentwire_dev-1.22.0/docs/pi-zai.md +222 -0
  26. agentwire_dev-1.22.0/docs/workflows.md +317 -0
  27. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/pyproject.toml +2 -0
  28. agentwire_dev-1.22.0/tests/integration/test_build_agent_command.py +237 -0
  29. agentwire_dev-1.22.0/tests/unit/test_workflows.py +467 -0
  30. agentwire_dev-1.21.0/tests/integration/test_build_agent_command.py +0 -63
  31. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/.github/FUNDING.yml +0 -0
  32. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  33. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  34. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/.github/ISSUE_TEMPLATE/question.md +0 -0
  35. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  36. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/.gitignore +0 -0
  37. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/CHANGELOG.md +0 -0
  38. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/CLA.md +0 -0
  39. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/CODE_OF_CONDUCT.md +0 -0
  40. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/CONTRIBUTING.md +0 -0
  41. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/Dockerfile.local +0 -0
  42. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/Dockerfile.runpod +0 -0
  43. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/LICENSE +0 -0
  44. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/README.md +0 -0
  45. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/RELEASING.md +0 -0
  46. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/SECURITY.md +0 -0
  47. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/SPONSORS.md +0 -0
  48. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/agents/__init__.py +0 -0
  49. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/agents/base.py +0 -0
  50. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/agents/tmux.py +0 -0
  51. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/bridges/__init__.py +0 -0
  52. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/bridges/telegram.py +0 -0
  53. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/cached_status.py +0 -0
  54. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/channels/__init__.py +0 -0
  55. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/channels/_template.py +0 -0
  56. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/channels/base.py +0 -0
  57. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/channels/discord.py +0 -0
  58. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/channels/email.py +0 -0
  59. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/channels/quo.py +0 -0
  60. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/channels/slack.py +0 -0
  61. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/channels/sms.py +0 -0
  62. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/channels/telegram.py +0 -0
  63. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/channels/webhook.py +0 -0
  64. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/cli_safety.py +0 -0
  65. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/completion.py +0 -0
  66. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/errors.py +0 -0
  67. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/history.py +0 -0
  68. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/hooks/__init__.py +0 -0
  69. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/hooks/agentwire-permission.sh +0 -0
  70. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/hooks/damage-control/__init__.py +0 -0
  71. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/hooks/damage-control/audit_logger.py +0 -0
  72. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/hooks/damage-control/bash-tool-damage-control.py +0 -0
  73. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/hooks/damage-control/edit-tool-damage-control.py +0 -0
  74. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/hooks/damage-control/rules/agentwire.yaml +0 -0
  75. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/hooks/damage-control/rules/aws.yaml +0 -0
  76. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/hooks/damage-control/rules/cloud-hosting.yaml +0 -0
  77. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/hooks/damage-control/rules/containers.yaml +0 -0
  78. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/hooks/damage-control/rules/core.yaml +0 -0
  79. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/hooks/damage-control/rules/databases.yaml +0 -0
  80. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/hooks/damage-control/rules/firebase.yaml +0 -0
  81. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/hooks/damage-control/rules/gcp.yaml +0 -0
  82. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/hooks/damage-control/rules/git.yaml +0 -0
  83. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/hooks/damage-control/rules/gws.yaml +0 -0
  84. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/hooks/damage-control/rules/infrastructure.yaml +0 -0
  85. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/hooks/damage-control/rules/remote.yaml +0 -0
  86. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/hooks/damage-control/write-tool-damage-control.py +0 -0
  87. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/hooks/idle-handler.sh +0 -0
  88. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/init_agentwire.py +0 -0
  89. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/listen.py +0 -0
  90. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/locking.py +0 -0
  91. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/network.py +0 -0
  92. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/onboarding.py +0 -0
  93. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/pane_manager.py +0 -0
  94. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/prompts/__init__.py +0 -0
  95. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/prompts/init.md +0 -0
  96. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/roles/__init__.py +0 -0
  97. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/roles/agentwire.md +0 -0
  98. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/roles/channel-admin.md +0 -0
  99. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/roles/chatbot.md +0 -0
  100. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/roles/discord-dm.md +0 -0
  101. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/roles/init.md +0 -0
  102. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/roles/orchestrator.md +0 -0
  103. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/roles/slack-dm.md +0 -0
  104. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/roles/task-runner.md +0 -0
  105. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/roles/voice.md +0 -0
  106. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/roles/worker.md +0 -0
  107. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/scheduler.py +0 -0
  108. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/agentwire-Echo--black.png +0 -0
  109. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/agentwire-Echo--transparent.png +0 -0
  110. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/agentwire-Echo.png +0 -0
  111. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/agentwire-email-banner.png +0 -0
  112. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/agentwire-splash-logo-layers--agentwire-text.png +0 -0
  113. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/agentwire-splash-logo-layers--echo-claw-fg.png +0 -0
  114. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/agentwire-splash-logo-layers--echo.png +0 -0
  115. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/agentwire-splash-logo-layers--full--transparent-top.png +0 -0
  116. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/agentwire-splash-logo-layers--full-black.png +0 -0
  117. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/agentwire-splash-logo-layers--telephone-fg.png +0 -0
  118. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/agentwire-splash-logo-layers--telephone.png +0 -0
  119. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/agentwire-splash-logo-layers--transparent-top.png +0 -0
  120. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/agentwire-splash-logo-layers--transparent.png +0 -0
  121. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/agentwire-splash-logo-layers--tree.png +0 -0
  122. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/agentwire-splash-logo-layers.png +0 -0
  123. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/favicon.png +0 -0
  124. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/machines/android.jpeg +0 -0
  125. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/machines/automaton.jpeg +0 -0
  126. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/machines/bot.jpeg +0 -0
  127. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/machines/cyborg.jpeg +0 -0
  128. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/machines/droid.jpeg +0 -0
  129. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/machines/drone.jpeg +0 -0
  130. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/machines/guardian.jpeg +0 -0
  131. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/machines/mech.jpeg +0 -0
  132. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/machines/probe.jpeg +0 -0
  133. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/machines/robot.jpeg +0 -0
  134. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/machines/sentinel.jpeg +0 -0
  135. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/machines/unit.jpeg +0 -0
  136. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/projects/blob.jpeg +0 -0
  137. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/projects/cloud.jpeg +0 -0
  138. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/projects/crystal.jpeg +0 -0
  139. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/projects/cyclops.jpeg +0 -0
  140. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/projects/flame.jpeg +0 -0
  141. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/projects/fuzzy.jpeg +0 -0
  142. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/projects/horned.jpeg +0 -0
  143. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/projects/moon.jpeg +0 -0
  144. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/projects/slime.jpeg +0 -0
  145. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/projects/star.jpeg +0 -0
  146. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/projects/tentacle.jpeg +0 -0
  147. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/projects/winged.jpeg +0 -0
  148. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/sessions/bear.jpeg +0 -0
  149. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/sessions/cat.jpeg +0 -0
  150. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/sessions/crown.jpeg +0 -0
  151. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/sessions/custom/agentwire-portal.png +0 -0
  152. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/sessions/custom/agentwire-tts.png +0 -0
  153. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/sessions/custom/agentwire.png +0 -0
  154. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/sessions/deer.jpeg +0 -0
  155. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/sessions/drone.jpeg +0 -0
  156. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/sessions/eagle.jpeg +0 -0
  157. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/sessions/fox.jpeg +0 -0
  158. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/sessions/hawk.jpeg +0 -0
  159. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/sessions/horse.jpeg +0 -0
  160. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/sessions/lion.jpeg +0 -0
  161. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/sessions/rabbit.jpeg +0 -0
  162. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/sessions/robot.jpeg +0 -0
  163. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/sessions/tiger.jpeg +0 -0
  164. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/icons/sessions/wolf.jpeg +0 -0
  165. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/js/.gitkeep +0 -0
  166. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/js/artifact-window.js +0 -0
  167. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/js/components/icon-picker.js +0 -0
  168. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/js/components/list-card.js +0 -0
  169. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/js/components/type-tag.js +0 -0
  170. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/js/icon-manager.js +0 -0
  171. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/js/session-window.js +0 -0
  172. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/js/sidebar/artifacts-section.js +0 -0
  173. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/js/sidebar/config-section.js +0 -0
  174. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/js/sidebar/machines-section.js +0 -0
  175. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/js/sidebar/projects-section.js +0 -0
  176. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/js/sidebar/scheduler-section.js +0 -0
  177. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/js/sidebar/services-section.js +0 -0
  178. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/js/sidebar/socials-section.js +0 -0
  179. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/js/sidebar.js +0 -0
  180. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/js/tile-manager.js +0 -0
  181. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/js/utils/auto-refresh.js +0 -0
  182. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/js/winbox.bundle.min.js +0 -0
  183. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/static/js/windows/chat-window.js +0 -0
  184. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/stt/__init__.py +0 -0
  185. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/stt/base.py +0 -0
  186. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/stt/server_backend.py +0 -0
  187. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/stt/stt_server.py +0 -0
  188. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/stt/whisperkit.py +0 -0
  189. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/tasks.py +0 -0
  190. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/templates/__init__.py +0 -0
  191. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/templates/base.html +0 -0
  192. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/templates/desktop.html +0 -0
  193. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/templates/email_notification.html +0 -0
  194. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/templates/tmux.conf +0 -0
  195. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/templating.py +0 -0
  196. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/tooldefs/aws.yaml +0 -0
  197. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/tooldefs/docker.yaml +0 -0
  198. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/tooldefs/gcp.yaml +0 -0
  199. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/tooldefs/gh.yaml +0 -0
  200. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/tooldefs/git.yaml +0 -0
  201. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/tooldefs/gws.yaml +0 -0
  202. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/tooldefs/kubectl.yaml +0 -0
  203. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/tooldefs/npm.yaml +0 -0
  204. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/tooldefs/terraform.yaml +0 -0
  205. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/tooldefs/uv.yaml +0 -0
  206. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/tts/__init__.py +0 -0
  207. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/tts/base.py +0 -0
  208. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/tts/engines/__init__.py +0 -0
  209. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/tts/engines/chatterbox.py +0 -0
  210. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/tts/engines/kokoro.py +0 -0
  211. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/tts/engines/qwen_base.py +0 -0
  212. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/tts/engines/qwen_custom.py +0 -0
  213. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/tts/engines/qwen_design.py +0 -0
  214. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/tts/engines/zonos.py +0 -0
  215. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/tts/registry.py +0 -0
  216. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/tts/runpod_handler.py +0 -0
  217. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/tts_server.py +0 -0
  218. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/tunnels.py +0 -0
  219. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/utils/__init__.py +0 -0
  220. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/utils/chunker.py +0 -0
  221. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/utils/file_io.py +0 -0
  222. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/utils/paths.py +0 -0
  223. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/utils/subprocess.py +0 -0
  224. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/validation.py +0 -0
  225. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/voiceclone.py +0 -0
  226. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/voices/darren.wav +0 -0
  227. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/voices/default.wav +0 -0
  228. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/voices/jessica.wav +0 -0
  229. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/voices/lisa.wav +0 -0
  230. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/voices/may.wav +0 -0
  231. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/agentwire/worktree.py +0 -0
  232. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/FEATURE-REQUESTS.md +0 -0
  233. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/FR-auto-mode-session-type.md +0 -0
  234. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/PORTAL.md +0 -0
  235. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/SHELL_ESCAPING.md +0 -0
  236. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/SPONSORS.md +0 -0
  237. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/TROUBLESHOOTING.md +0 -0
  238. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/README.md +0 -0
  239. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-agent-hot-swap.md +0 -0
  240. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-ambient-context-stream.md +0 -0
  241. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-ambient-listening-mode.md +0 -0
  242. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-audio-cues.md +0 -0
  243. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-checkpoint-commits.md +0 -0
  244. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-context-compression-protocol.md +0 -0
  245. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-context-window-gauge.md +0 -0
  246. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-contextual-bookmarks.md +0 -0
  247. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-conversation-archaeology.md +0 -0
  248. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-cost-tracking-dashboard.md +0 -0
  249. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-cross-session-events.md +0 -0
  250. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-delegation-replay.md +0 -0
  251. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-device-session-tethering.md +0 -0
  252. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-failure-memory.md +0 -0
  253. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-notification-escalation.md +0 -0
  254. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-periodic-voice-briefings.md +0 -0
  255. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-presence-aware-sessions.md +0 -0
  256. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-session-drift-detection.md +0 -0
  257. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-session-energy-model.md +0 -0
  258. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-session-handshake.md +0 -0
  259. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-session-momentum.md +0 -0
  260. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-session-replay.md +0 -0
  261. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-session-snapshots.md +0 -0
  262. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-session-templates.md +0 -0
  263. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-session-thermal-throttling.md +0 -0
  264. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-smart-session-routing.md +0 -0
  265. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-spatial-voice-mixing.md +0 -0
  266. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-speculative-execution.md +0 -0
  267. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-task-pipeline-chaining.md +0 -0
  268. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-task-pivot-protocol.md +0 -0
  269. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-task-time-budgets.md +0 -0
  270. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-voice-activity-zones.md +0 -0
  271. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-voice-breakpoints.md +0 -0
  272. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-voice-code-review.md +0 -0
  273. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-voice-command-undo.md +0 -0
  274. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-voice-handoff.md +0 -0
  275. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-voice-identity.md +0 -0
  276. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-voice-interrupts-v2.md +0 -0
  277. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-voice-interrupts.md +0 -0
  278. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-voice-macros.md +0 -0
  279. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-voice-transcript-logs.md +0 -0
  280. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-watchdog-mode.md +0 -0
  281. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-worker-fencing.md +0 -0
  282. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-worker-file-coordination.md +0 -0
  283. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-worker-health-dashboard.md +0 -0
  284. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-worker-heartbeat-watchdog.md +0 -0
  285. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-worker-progress-streaming.md +0 -0
  286. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-worker-proof-of-work.md +0 -0
  287. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-worker-warmup.md +0 -0
  288. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/brainstorms/idea-x-api-integration.md +0 -0
  289. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/channels.md +0 -0
  290. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/claude-code-auto-mode.md +0 -0
  291. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/critical-analysis.md +0 -0
  292. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/demo-script.md +0 -0
  293. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/gws-google-workspace-cli.md +0 -0
  294. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/hammerspoon.md +0 -0
  295. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/issues/pending-code-changes.md +0 -0
  296. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/issues/projects-dedup-ignores-machine.md +0 -0
  297. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/issues/remote-tts-session-detection.md +0 -0
  298. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/issues/tmux-config-onboarding.md +0 -0
  299. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/issues/tmux-paste-freeze.md +0 -0
  300. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/issues/tmux-recommended-config.md +0 -0
  301. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/logo.png +0 -0
  302. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/notification-hooks.md +0 -0
  303. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/progressive-loading-pattern.md +0 -0
  304. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/prompts/agentwire-website.md +0 -0
  305. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/releasing.md +0 -0
  306. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/remote-access.md +0 -0
  307. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/remote-machines.md +0 -0
  308. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/runpod-tts.md +0 -0
  309. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/scheduled-workloads.md +0 -0
  310. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/security/damage-control-migration.md +0 -0
  311. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/security/damage-control.md +0 -0
  312. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/tmux-hooks.md +0 -0
  313. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/tts-self-hosted.md +0 -0
  314. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/docs/youtube-channel.md +0 -0
  315. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/requirements-tts.txt +0 -0
  316. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/tests/conftest.py +0 -0
  317. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/tests/e2e/test_portal_ui.py +0 -0
  318. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/tests/fixtures/sample_agentwire.yml +0 -0
  319. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/tests/fixtures/sample_config.yaml +0 -0
  320. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/tests/fixtures/sample_scheduler.yaml +0 -0
  321. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/tests/integration/test_channels_cli.py +0 -0
  322. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/tests/integration/test_cli_commands.py +0 -0
  323. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/tests/integration/test_cli_output.py +0 -0
  324. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/tests/integration/test_mcp_tools.py +0 -0
  325. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/tests/integration/test_portal_api.py +0 -0
  326. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/tests/integration/test_scheduler_board.py +0 -0
  327. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/tests/unit/test_channels.py +0 -0
  328. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/tests/unit/test_cli_safety.py +0 -0
  329. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/tests/unit/test_config.py +0 -0
  330. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/tests/unit/test_file_io.py +0 -0
  331. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/tests/unit/test_history.py +0 -0
  332. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/tests/unit/test_locking.py +0 -0
  333. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/tests/unit/test_mcp_server.py +0 -0
  334. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/tests/unit/test_project_config.py +0 -0
  335. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/tests/unit/test_roles.py +0 -0
  336. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/tests/unit/test_scheduler.py +0 -0
  337. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/tests/unit/test_server_async.py +0 -0
  338. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/tests/unit/test_server_pure.py +0 -0
  339. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/tests/unit/test_tasks.py +0 -0
  340. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/tests/unit/test_templating.py +0 -0
  341. {agentwire_dev-1.21.0 → agentwire_dev-1.22.0}/tests/unit/test_worktree.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentwire-dev
3
- Version: 1.21.0
3
+ Version: 1.22.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.21.0"
3
+ __version__ = "1.22.0"
@@ -2,7 +2,7 @@
2
2
 
3
3
  import argparse
4
4
  import base64
5
- from dataclasses import dataclass
5
+ from dataclasses import dataclass, field
6
6
  import datetime
7
7
  import importlib.resources
8
8
  import json
@@ -81,15 +81,75 @@ class AgentCommand:
81
81
  """Result of building an agent command."""
82
82
  command: str # The shell command to execute
83
83
  temp_file: str | None = None # Temp file to clean up after agent starts
84
+ env: dict[str, str] = field(default_factory=dict) # Secrets to inject via tmux set-environment (keeps keys out of `ps`)
85
+
86
+
87
+ def inject_session_env(session: str, env: dict[str, str], remote_host: str | None = None) -> None:
88
+ """Set env vars on a tmux session so panes inherit them without exposing secrets.
89
+
90
+ Using `tmux set-environment -t <session>` keeps API keys out of the command
91
+ string (which would otherwise be visible in `ps auxwww` and shell history).
92
+ """
93
+ if not env:
94
+ return
95
+ for key, value in env.items():
96
+ if remote_host:
97
+ subprocess.run(
98
+ ["ssh", remote_host, "tmux", "set-environment", "-t",
99
+ shlex.quote(session), shlex.quote(key), shlex.quote(value)],
100
+ check=False,
101
+ )
102
+ else:
103
+ subprocess.run(
104
+ ["tmux", "set-environment", "-t", session, key, value],
105
+ check=False,
106
+ )
107
+
108
+
109
+ def parse_env_args(env_args: list[str] | None) -> dict[str, str]:
110
+ """Parse repeated `--env KEY=VAL` flags into a dict.
111
+
112
+ Raises SystemExit via argparse pattern if an entry lacks `=`.
113
+ """
114
+ if not env_args:
115
+ return {}
116
+ result: dict[str, str] = {}
117
+ for entry in env_args:
118
+ if "=" not in entry:
119
+ print(f"Error: --env expects KEY=VAL, got {entry!r}", file=sys.stderr)
120
+ sys.exit(2)
121
+ key, value = entry.split("=", 1)
122
+ if not key:
123
+ print(f"Error: --env KEY cannot be empty (got {entry!r})", file=sys.stderr)
124
+ sys.exit(2)
125
+ result[key] = value
126
+ return result
127
+
128
+
129
+ def build_session_env_shell_fragment(session: str, env: dict[str, str]) -> str:
130
+ """Build a shell fragment of `tmux set-environment` calls for chained remote commands.
131
+
132
+ Returns a trailing `&& ` string ready to splice into an ssh-executed compound
133
+ command. Empty string if no env.
134
+ """
135
+ if not env:
136
+ return ""
137
+ parts = []
138
+ for key, value in env.items():
139
+ parts.append(
140
+ f"tmux set-environment -t {shlex.quote(session)} "
141
+ f"{shlex.quote(key)} {shlex.quote(value)}"
142
+ )
143
+ return " && ".join(parts) + " && "
84
144
 
85
145
 
86
146
  def build_agent_command(session_type: str, roles: list[RoleConfig] | None = None, model: str | None = None) -> AgentCommand:
87
147
  """Build the agent command for a session.
88
148
 
89
149
  Args:
90
- session_type: Session type (e.g., "claude-bypass", "claude-auto", "bare")
150
+ session_type: Session type (e.g., "claude-bypass", "claude-auto", "pi-zai", "bare")
91
151
  roles: Optional list of roles to apply
92
- model: Optional model override (e.g., "haiku", "sonnet", "opus")
152
+ model: Optional model override (e.g., "haiku", "sonnet", "opus", "glm-5")
93
153
 
94
154
  Returns:
95
155
  AgentCommand with the command string and metadata
@@ -101,6 +161,49 @@ def build_agent_command(session_type: str, roles: list[RoleConfig] | None = None
101
161
  # Merge roles if provided
102
162
  merged = merge_roles(roles) if roles else None
103
163
 
164
+ # === Pi coding agent via Z.AI GLM ===
165
+ if session_type.startswith("pi-zai"):
166
+ config = load_config()
167
+ zai = config.get("zai", {})
168
+ pi_config = config.get("pi", {})
169
+
170
+ # Pi reads ZAI_API_KEY from env. We inject it via `tmux set-environment`
171
+ # (see inject_session_env) so the key never appears in `ps auxwww`.
172
+ pi_binary = pi_config.get("binary", "pi")
173
+ default_model = pi_config.get("default_model", "glm-5")
174
+
175
+ parts = [pi_binary, "--provider", "zai"]
176
+ parts.extend(["--model", model or default_model])
177
+
178
+ # Permission variants (pi has no permission system to bypass; variants
179
+ # translate to pi's --tools whitelist instead)
180
+ temp_file = None
181
+ if session_type == "pi-zai-restricted":
182
+ parts.extend(["--tools", "read,grep,find,bash"])
183
+ elif session_type == "pi-zai-readonly":
184
+ parts.extend(["--tools", "read,grep,find"])
185
+ elif merged and merged.tools:
186
+ # Translate Claude tool names to pi's lowercase tool names.
187
+ # Pi only supports: read, bash, edit, write, grep, find, ls
188
+ pi_valid = {"read", "bash", "edit", "write", "grep", "find", "ls"}
189
+ pi_tools = [t.lower() for t in merged.tools if t.lower() in pi_valid]
190
+ if pi_tools:
191
+ parts.extend(["--tools", ",".join(pi_tools)])
192
+
193
+ # Role-based system prompt (skipped for restricted/readonly — those are curated contexts)
194
+ if merged and merged.instructions and session_type not in ("pi-zai-restricted", "pi-zai-readonly"):
195
+ f = tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False)
196
+ f.write(merged.instructions)
197
+ f.close()
198
+ temp_file = f.name
199
+ parts.append(f'--append-system-prompt "$(<{temp_file})"')
200
+
201
+ return AgentCommand(
202
+ command=" ".join(parts),
203
+ temp_file=temp_file,
204
+ env={"ZAI_API_KEY": zai.get("api_key", "")},
205
+ )
206
+
104
207
  # === Claude Code ===
105
208
  if session_type.startswith("claude"):
106
209
  parts = ["claude"]
@@ -277,6 +380,27 @@ def tmux_session_exists(name: str) -> bool:
277
380
  return result.returncode == 0
278
381
 
279
382
 
383
+ def wait_for_shell_prompt(target: str, timeout: float = 2.0) -> None:
384
+ """Poll tmux capture-pane until the shell has drawn a prompt.
385
+
386
+ Prevents a race where send-keys fires before the shell is ready, causing
387
+ the command to appear in the pre-prompt buffer and again after the prompt
388
+ renders (looks like it ran twice).
389
+ """
390
+ import time
391
+ deadline = time.monotonic() + timeout
392
+ while time.monotonic() < deadline:
393
+ result = subprocess.run(
394
+ ["tmux", "capture-pane", "-t", target, "-p"],
395
+ capture_output=True, text=True,
396
+ )
397
+ if result.returncode == 0 and any(
398
+ c in result.stdout for c in ("$", "%", "#", "❯", "➜", ">")
399
+ ):
400
+ return
401
+ time.sleep(0.05)
402
+
403
+
280
404
  def _get_session_project_path(session: str) -> Path | None:
281
405
  """Get a session's project path from its tmux working directory.
282
406
 
@@ -3403,8 +3527,7 @@ def cmd_new(args) -> int:
3403
3527
 
3404
3528
  # Build agent command
3405
3529
  agent = build_agent_command(session_type, roles if roles else None)
3406
-
3407
-
3530
+ agent.env.update(parse_env_args(getattr(args, 'env', None)))
3408
3531
 
3409
3532
  agent_cmd = agent.command
3410
3533
 
@@ -3425,12 +3548,15 @@ def cmd_new(args) -> int:
3425
3548
  except Exception as e:
3426
3549
  print(f"Warning: Failed to write system prompt to remote: {e}", file=sys.stderr)
3427
3550
 
3551
+ env_fragment = build_session_env_shell_fragment(session_name, agent.env)
3552
+
3428
3553
  # Create session - Agent starts immediately if not bare
3429
3554
  if agent_cmd:
3430
3555
  create_cmd = (
3431
3556
  f"tmux new-session -d -s {shlex.quote(session_name)} -c {shlex.quote(remote_path)} && "
3432
3557
  f"tmux send-keys -t {shlex.quote(session_name)} 'cd {shlex.quote(remote_path)}' Enter && "
3433
3558
  f"sleep 0.1 && "
3559
+ f"{env_fragment}"
3434
3560
  f"tmux send-keys -t {shlex.quote(session_name)} {shlex.quote(agent_cmd)} Enter"
3435
3561
  )
3436
3562
  else:
@@ -3571,9 +3697,13 @@ def cmd_new(args) -> int:
3571
3697
  # Build agent command
3572
3698
  model_override = getattr(args, 'model', None)
3573
3699
  agent = build_agent_command(session_type, roles if roles else None, model=model_override)
3700
+ agent.env.update(parse_env_args(getattr(args, 'env', None)))
3574
3701
 
3575
3702
  agent_cmd = agent.command
3576
3703
 
3704
+ # Inject secrets via tmux set-environment (keeps keys out of `ps`)
3705
+ inject_session_env(session_name, agent.env)
3706
+
3577
3707
  # Start agent command if not bare
3578
3708
  if agent_cmd:
3579
3709
  subprocess.run(
@@ -4071,10 +4201,17 @@ def cmd_spawn(args) -> int:
4071
4201
 
4072
4202
  # Build agent command
4073
4203
  agent = build_agent_command(session_type_str, roles if roles else None)
4204
+ agent.env.update(parse_env_args(getattr(args, 'env', None)))
4074
4205
 
4075
4206
  agent_cmd = agent.command
4076
4207
 
4077
4208
  try:
4209
+ # Inject secrets onto the parent session before spawning the pane so the
4210
+ # new pane inherits them from tmux (avoids putting keys in `ps`).
4211
+ parent_session = session or pane_manager.get_current_session()
4212
+ if parent_session:
4213
+ inject_session_env(parent_session, agent.env)
4214
+
4078
4215
  # Spawn pane first to get the pane index
4079
4216
  pane_index = pane_manager.spawn_worker_pane(
4080
4217
  session=session,
@@ -4451,12 +4588,15 @@ def cmd_recreate(args) -> int:
4451
4588
 
4452
4589
  # Build agent command using the standard function
4453
4590
  agent = build_agent_command(session_type_str)
4591
+ agent.env.update(parse_env_args(getattr(args, 'env', None)))
4454
4592
  agent_cmd = agent.command
4593
+ env_fragment = build_session_env_shell_fragment(session_name, agent.env)
4455
4594
 
4456
4595
  create_cmd = (
4457
4596
  f"tmux new-session -d -s {shlex.quote(session_name)} -c {shlex.quote(session_path)} && "
4458
4597
  f"tmux send-keys -t {shlex.quote(session_name)} 'cd {shlex.quote(session_path)}' Enter && "
4459
4598
  f"sleep 0.1 && "
4599
+ f"{env_fragment}"
4460
4600
  f"tmux send-keys -t {shlex.quote(session_name)} {shlex.quote(agent_cmd)} Enter"
4461
4601
  )
4462
4602
 
@@ -4553,6 +4693,7 @@ def cmd_recreate(args) -> int:
4553
4693
 
4554
4694
  # Build agent command
4555
4695
  agent = build_agent_command(session_type_str, roles)
4696
+ agent.env.update(parse_env_args(getattr(args, 'env', None)))
4556
4697
 
4557
4698
  agent_cmd = agent.command
4558
4699
 
@@ -4569,6 +4710,9 @@ def cmd_recreate(args) -> int:
4569
4710
  )
4570
4711
  time.sleep(0.1)
4571
4712
 
4713
+ # Inject secrets via tmux set-environment (keeps keys out of `ps`)
4714
+ inject_session_env(session_name, agent.env)
4715
+
4572
4716
  # Start the agent with appropriate command
4573
4717
  if agent_cmd:
4574
4718
  subprocess.run(
@@ -4633,6 +4777,7 @@ def cmd_worktree(args) -> int:
4633
4777
  'force': False, 'bare': False, 'restricted': False, 'prompted': False,
4634
4778
  'type': getattr(args, 'type', None), 'roles': getattr(args, 'roles', None),
4635
4779
  'instructions': None, 'persist': False,
4780
+ 'env': getattr(args, 'env', None),
4636
4781
  })())
4637
4782
 
4638
4783
  # If worktree already exists, reattach
@@ -4825,13 +4970,16 @@ def cmd_fork(args) -> int:
4825
4970
 
4826
4971
  # Build agent command
4827
4972
  agent = build_agent_command(session_type_str, roles)
4973
+ agent.env.update(parse_env_args(getattr(args, 'env', None)))
4828
4974
 
4829
4975
  agent_cmd = agent.command
4976
+ env_fragment = build_session_env_shell_fragment(target_session, agent.env)
4830
4977
 
4831
4978
  create_session_cmd = (
4832
4979
  f"tmux new-session -d -s {shlex.quote(target_session)} -c {shlex.quote(target_path)} && "
4833
4980
  f"tmux send-keys -t {shlex.quote(target_session)} 'cd {shlex.quote(target_path)}' Enter && "
4834
4981
  f"sleep 0.1 && "
4982
+ f"{env_fragment}"
4835
4983
  f"tmux send-keys -t {shlex.quote(target_session)} {shlex.quote(agent_cmd)} Enter"
4836
4984
  )
4837
4985
 
@@ -4978,6 +5126,10 @@ def cmd_fork(args) -> int:
4978
5126
 
4979
5127
  # Build agent command
4980
5128
  agent = build_agent_command(session_type_str, roles)
5129
+ agent.env.update(parse_env_args(getattr(args, 'env', None)))
5130
+
5131
+ # Inject secrets via tmux set-environment (keeps keys out of `ps`)
5132
+ inject_session_env(target_session, agent.env)
4981
5133
 
4982
5134
  agent_cmd = agent.command
4983
5135
  if agent_cmd:
@@ -5091,7 +5243,12 @@ def cmd_fork(args) -> int:
5091
5243
 
5092
5244
  # Build agent command
5093
5245
  agent = build_agent_command(session_type_str, roles)
5246
+ agent.env.update(parse_env_args(getattr(args, 'env', None)))
5094
5247
  agent_cmd = agent.command
5248
+
5249
+ # Inject secrets via tmux set-environment (keeps keys out of `ps`)
5250
+ inject_session_env(target_session, agent.env)
5251
+
5095
5252
  if agent_cmd:
5096
5253
  subprocess.run(
5097
5254
  ["tmux", "send-keys", "-t", target_session, agent_cmd, "Enter"],
@@ -5689,8 +5846,12 @@ def cmd_dev(args) -> int:
5689
5846
  "tmux", "new-session", "-d", "-s", session_name, "-c", str(project_dir),
5690
5847
  ])
5691
5848
 
5849
+ # Inject secrets via tmux set-environment (keeps keys out of `ps`)
5850
+ inject_session_env(session_name, agent.env)
5851
+
5692
5852
  # Start agent with agentwire config
5693
5853
  if agent_cmd:
5854
+ wait_for_shell_prompt(session_name)
5694
5855
  subprocess.run([
5695
5856
  "tmux", "send-keys", "-t", session_name, agent_cmd, "Enter",
5696
5857
  ])
@@ -6047,6 +6208,23 @@ def cmd_doctor(args) -> int:
6047
6208
  print(" [..] claude: not found (optional, use --bare sessions or other agents)")
6048
6209
  print(" Install: https://github.com/anthropics/claude-code")
6049
6210
 
6211
+ # Check Pi coding agent (optional, for pi-zai session types)
6212
+ pi_path = shutil.which("pi")
6213
+ if pi_path:
6214
+ try:
6215
+ # Pi prints --version to stderr, so merge with stdout
6216
+ result = subprocess.run(
6217
+ [pi_path, "--version"],
6218
+ capture_output=True, text=True, timeout=5,
6219
+ )
6220
+ pi_version = (result.stdout + result.stderr).strip()
6221
+ print(f" [ok] pi: {pi_path} (v{pi_version})")
6222
+ except Exception:
6223
+ print(f" [ok] pi: {pi_path}")
6224
+ else:
6225
+ print(" [..] pi: not found (optional, required for pi-zai session types)")
6226
+ print(" Install: npm install -g @mariozechner/pi-coding-agent")
6227
+
6050
6228
  # 3. Check AgentWire scripts
6051
6229
  print("\nChecking AgentWire scripts...")
6052
6230
 
@@ -9832,11 +10010,12 @@ def main() -> int:
9832
10010
  new_parser.add_argument("-p", "--path", help="Working directory (default: ~/projects/<name>)")
9833
10011
  new_parser.add_argument("-f", "--force", action="store_true", help="Replace existing session")
9834
10012
  # Session type
9835
- new_parser.add_argument("--type", help="Session type (bare, claude-bypass, claude-prompted, claude-restricted, standard, worker, voice)")
10013
+ new_parser.add_argument("--type", help="Session type (bare, claude-bypass, claude-prompted, claude-restricted, pi-zai, pi-zai-restricted, pi-zai-readonly, standard, worker, voice)")
9836
10014
  # Roles
9837
10015
  new_parser.add_argument("--roles", help="Comma-separated list of roles (preserves existing config, defaults to agentwire for new projects)")
9838
10016
  new_parser.add_argument("--model", help="Model override (e.g., haiku, sonnet, opus)")
9839
10017
  new_parser.add_argument("--persist", action="store_true", help="Write --type/--roles to .agentwire.yml (default: session-level override only)")
10018
+ new_parser.add_argument("--env", action="append", metavar="KEY=VAL", help="Inject env var via `tmux set-environment` (repeatable, keeps secrets out of `ps`)")
9840
10019
  new_parser.add_argument("--json", action="store_true", help="Output as JSON")
9841
10020
  new_parser.set_defaults(func=cmd_new)
9842
10021
 
@@ -9867,10 +10046,11 @@ def main() -> int:
9867
10046
  spawn_parser.add_argument("-s", "--session", help="Target session (default: auto-detect)")
9868
10047
  spawn_parser.add_argument("--cwd", help="Working directory (default: current)")
9869
10048
  spawn_parser.add_argument("--branch", "-b", help="Create worktree on this branch for isolated commits")
9870
- spawn_parser.add_argument("--type", help="Session type (claude-bypass, claude-prompted, claude-restricted)")
10049
+ spawn_parser.add_argument("--type", help="Session type (claude-bypass, claude-prompted, claude-restricted, pi-zai, pi-zai-restricted, pi-zai-readonly)")
9871
10050
  spawn_parser.add_argument("--roles", default="worker", help="Comma-separated roles (default: worker)")
9872
10051
  spawn_parser.add_argument("--no-wait", action="store_true", help="Don't wait for worker to be ready (default: wait up to 30s)")
9873
10052
  spawn_parser.add_argument("--timeout", type=int, default=30, help="Seconds to wait for worker ready (default: 30)")
10053
+ spawn_parser.add_argument("--env", action="append", metavar="KEY=VAL", help="Inject env var onto parent session (repeatable)")
9874
10054
  spawn_parser.add_argument("--json", action="store_true", help="Output as JSON")
9875
10055
  spawn_parser.set_defaults(func=cmd_spawn)
9876
10056
 
@@ -9905,7 +10085,8 @@ def main() -> int:
9905
10085
  recreate_parser = subparsers.add_parser("recreate", help="Destroy and recreate session with fresh worktree")
9906
10086
  recreate_parser.add_argument("-s", "--session", required=True, help="Session name (project/branch or project/branch@machine)")
9907
10087
  # Session type
9908
- recreate_parser.add_argument("--type", help="Session type (bare, claude-bypass, claude-prompted, claude-restricted, standard, worker, voice)")
10088
+ recreate_parser.add_argument("--type", help="Session type (bare, claude-bypass, claude-prompted, claude-restricted, pi-zai, pi-zai-restricted, pi-zai-readonly, standard, worker, voice)")
10089
+ recreate_parser.add_argument("--env", action="append", metavar="KEY=VAL", help="Inject env var via `tmux set-environment` (repeatable)")
9909
10090
  recreate_parser.add_argument("--json", action="store_true", help="Output as JSON")
9910
10091
  recreate_parser.set_defaults(func=cmd_recreate)
9911
10092
 
@@ -9914,8 +10095,9 @@ def main() -> int:
9914
10095
  fork_parser.add_argument("-s", "--source", required=True, help="Source session (project or project/branch)")
9915
10096
  fork_parser.add_argument("-t", "--target", required=True, help="Target session (must include branch: project/new-branch)")
9916
10097
  # Session type
9917
- fork_parser.add_argument("--type", help="Session type (bare, claude-bypass, claude-prompted, claude-restricted, standard, worker, voice)")
10098
+ fork_parser.add_argument("--type", help="Session type (bare, claude-bypass, claude-prompted, claude-restricted, pi-zai, pi-zai-restricted, pi-zai-readonly, standard, worker, voice)")
9918
10099
  fork_parser.add_argument("--commit", metavar="REF", help="Fork from this commit/ref instead of HEAD (e.g. abc123, main~5)")
10100
+ fork_parser.add_argument("--env", action="append", metavar="KEY=VAL", help="Inject env var via `tmux set-environment` (repeatable)")
9919
10101
  fork_parser.add_argument("--json", action="store_true", help="Output as JSON")
9920
10102
  fork_parser.set_defaults(func=cmd_fork)
9921
10103
 
@@ -9929,6 +10111,7 @@ def main() -> int:
9929
10111
  wt_parser.add_argument("--project", "-p", help="Path to git repo (default: from config or cwd)")
9930
10112
  wt_parser.add_argument("--type", help="Session type override")
9931
10113
  wt_parser.add_argument("--roles", help="Comma-separated role names")
10114
+ wt_parser.add_argument("--env", action="append", metavar="KEY=VAL", help="Inject env var via `tmux set-environment` (repeatable)")
9932
10115
  wt_parser.add_argument("--json", action="store_true", help="Output as JSON")
9933
10116
  wt_parser.set_defaults(func=cmd_worktree)
9934
10117
 
@@ -9938,6 +10121,70 @@ def main() -> int:
9938
10121
  )
9939
10122
  dev_parser.set_defaults(func=cmd_dev)
9940
10123
 
10124
+ # === workflow command group ===
10125
+ from agentwire.workflows.cli import (
10126
+ cmd_workflow_history,
10127
+ cmd_workflow_list,
10128
+ cmd_workflow_run,
10129
+ cmd_workflow_show,
10130
+ cmd_workflow_validate,
10131
+ )
10132
+
10133
+ workflow_parser = subparsers.add_parser("workflow", help="Pi workflow engine")
10134
+ workflow_subparsers = workflow_parser.add_subparsers(dest="workflow_command")
10135
+
10136
+ wf_list = workflow_subparsers.add_parser("list", help="List discoverable workflows")
10137
+ wf_list.add_argument("--json", action="store_true", help="Output as JSON")
10138
+ wf_list.set_defaults(func=cmd_workflow_list)
10139
+
10140
+ wf_validate = workflow_subparsers.add_parser(
10141
+ "validate", help="Validate a workflow YAML without running it"
10142
+ )
10143
+ wf_validate.add_argument("workflow", help="Workflow name or path to YAML")
10144
+ wf_validate.set_defaults(func=cmd_workflow_validate)
10145
+
10146
+ wf_run = workflow_subparsers.add_parser("run", help="Execute a workflow")
10147
+ wf_run.add_argument("workflow", help="Workflow name or path to YAML")
10148
+ wf_run.add_argument(
10149
+ "--input", action="append", metavar="KEY=VALUE",
10150
+ help="Workflow input (repeatable). Overrides --input-file."
10151
+ )
10152
+ wf_run.add_argument(
10153
+ "--input-file", metavar="PATH",
10154
+ help="JSON file with inputs (object mapping name → value)"
10155
+ )
10156
+ wf_run.add_argument("--dry-run", action="store_true", help="Print plan without running")
10157
+ wf_run.add_argument("--verbose", "-v", action="store_true", help="Verbose output")
10158
+ wf_run.add_argument("--json", action="store_true", help="Output as JSON")
10159
+ wf_run.set_defaults(func=cmd_workflow_run)
10160
+
10161
+ wf_history = workflow_subparsers.add_parser(
10162
+ "history", help="List past workflow runs"
10163
+ )
10164
+ wf_history.add_argument(
10165
+ "--workflow", metavar="NAME", help="Filter by workflow name"
10166
+ )
10167
+ wf_history.add_argument(
10168
+ "--limit", type=int, default=20, help="Max runs to show (default: 20)"
10169
+ )
10170
+ wf_history.add_argument("--json", action="store_true", help="Output as JSON")
10171
+ wf_history.set_defaults(func=cmd_workflow_history)
10172
+
10173
+ wf_show = workflow_subparsers.add_parser(
10174
+ "show", help="Inspect a past workflow run"
10175
+ )
10176
+ wf_show.add_argument("run_id", help="Run ID (from `workflow history`)")
10177
+ wf_show.add_argument(
10178
+ "--events", action="store_true",
10179
+ help="Dump raw event JSONL for all nodes",
10180
+ )
10181
+ wf_show.add_argument(
10182
+ "--node", metavar="ID",
10183
+ help="Filter events to one node id (implies --events)",
10184
+ )
10185
+ wf_show.add_argument("--json", action="store_true", help="Output as JSON")
10186
+ wf_show.set_defaults(func=cmd_workflow_show)
10187
+
9941
10188
  # === listen command group ===
9942
10189
  listen_parser = subparsers.add_parser("listen", help="Voice input recording")
9943
10190
  listen_parser.add_argument(
@@ -74,6 +74,7 @@ class ProjectsConfig:
74
74
 
75
75
  dir: Path = field(default_factory=lambda: Path.home() / "projects")
76
76
  worktrees: WorktreesConfig = field(default_factory=WorktreesConfig)
77
+ extra: list = field(default_factory=list)
77
78
 
78
79
  def __post_init__(self):
79
80
  self.dir = _expand_path(self.dir) or Path.home() / "projects"
@@ -355,6 +356,7 @@ def _dict_to_config(data: dict) -> Config:
355
356
  projects = ProjectsConfig(
356
357
  dir=projects_data.get("dir", "~/projects"),
357
358
  worktrees=worktrees,
359
+ extra=projects_data.get("extra", []),
358
360
  )
359
361
 
360
362
  # TTS
@@ -2457,6 +2457,141 @@ def desktop_layout(windows: list[dict]) -> str:
2457
2457
  return f"Failed to apply layout: {data.get('error', 'Unknown error')}"
2458
2458
 
2459
2459
 
2460
+ @mcp.tool()
2461
+ def portal_notify(text: str, session: str | None = None, priority: str = "normal") -> str:
2462
+ """Post a toast notification to the portal desktop.
2463
+
2464
+ Creates a persistent visual notification in the bottom-right of the portal.
2465
+ Clicking the toast opens the notifications session for interactive chat.
2466
+
2467
+ Args:
2468
+ text: Notification message text.
2469
+ session: Session name this notification relates to (shown as badge).
2470
+ priority: 'normal' or 'high' (high gets accent border).
2471
+
2472
+ Returns:
2473
+ Notification ID or error description.
2474
+ """
2475
+ body = {"text": text, "priority": priority}
2476
+ if session:
2477
+ body["session"] = session
2478
+ data = _portal_request("POST", "/api/desktop/notification", body)
2479
+ if data.get("success"):
2480
+ return f"Notification posted (id: {data.get('id')})."
2481
+ return f"Failed to post notification: {data.get('error', 'Unknown error')}"
2482
+
2483
+
2484
+
2485
+ # =============================================================================
2486
+ # Workflow engine tools
2487
+ # =============================================================================
2488
+
2489
+
2490
+ @mcp.tool()
2491
+ def workflow_list() -> str:
2492
+ """List discoverable pi workflows.
2493
+
2494
+ Returns workflow names, descriptions, and node counts. Workflows live in
2495
+ ~/.agentwire/workflows/defs/ or the bundled examples dir.
2496
+ """
2497
+ data = run_agentwire_cmd(["workflow", "list"])
2498
+ if not data.get("success"):
2499
+ return f"Failed to list workflows: {data.get('error', 'Unknown error')}"
2500
+
2501
+ items = data.get("items", [])
2502
+ if not items:
2503
+ return "No workflows found."
2504
+
2505
+ lines = ["Available workflows:"]
2506
+ for wf in items:
2507
+ name = wf.get("name", "?")
2508
+ desc = wf.get("description", "")
2509
+ count = len(wf.get("nodes", []))
2510
+ lines.append(f" - {name} ({count} node{'s' if count != 1 else ''})"
2511
+ + (f" — {desc}" if desc else ""))
2512
+ return "\n".join(lines)
2513
+
2514
+
2515
+ @mcp.tool()
2516
+ def workflow_validate(name_or_path: str) -> str:
2517
+ """Parse + validate a workflow YAML without running it.
2518
+
2519
+ Args:
2520
+ name_or_path: Workflow name (e.g. "echo-chain") or path to YAML file.
2521
+ """
2522
+ data = run_agentwire_cmd(["workflow", "validate", name_or_path])
2523
+ if data.get("success"):
2524
+ return data.get("output", f"Workflow {name_or_path!r} is valid.")
2525
+ return f"Validation failed: {data.get('output', data.get('error', 'Unknown error'))}"
2526
+
2527
+
2528
+ @mcp.tool()
2529
+ def workflow_run(
2530
+ name: str,
2531
+ inputs: dict | None = None,
2532
+ dry_run: bool = False,
2533
+ ) -> dict:
2534
+ """Execute a pi workflow end-to-end.
2535
+
2536
+ Args:
2537
+ name: Workflow name or path to YAML.
2538
+ inputs: Optional map of input name → value, forwarded as --input KEY=VAL.
2539
+ dry_run: If true, print the execution plan without invoking pi.
2540
+
2541
+ Returns:
2542
+ Full run result as a dict: workflow, run_id, status
2543
+ (success|partial|failure), duration_ms, error, nodes[].
2544
+ """
2545
+ args = ["workflow", "run", name]
2546
+ for key, value in (inputs or {}).items():
2547
+ args.extend(["--input", f"{key}={value}"])
2548
+ if dry_run:
2549
+ args.append("--dry-run")
2550
+ # pi calls routinely take minutes; default 30s is not enough.
2551
+ return run_agentwire_cmd(args, timeout=600)
2552
+
2553
+
2554
+ @mcp.tool()
2555
+ def workflow_history(workflow: str | None = None, limit: int = 20) -> str:
2556
+ """List past workflow runs, newest first.
2557
+
2558
+ Args:
2559
+ workflow: Filter to a single workflow name (optional).
2560
+ limit: Max runs to return (default 20).
2561
+ """
2562
+ args = ["workflow", "history", "--limit", str(limit)]
2563
+ if workflow:
2564
+ args.extend(["--workflow", workflow])
2565
+ data = run_agentwire_cmd(args)
2566
+ if not data.get("success"):
2567
+ return f"Failed to list runs: {data.get('error', 'Unknown error')}"
2568
+
2569
+ runs = data.get("items", [])
2570
+ if not runs:
2571
+ return "No past runs found."
2572
+
2573
+ lines = ["Past runs:"]
2574
+ for run in runs:
2575
+ rid = run.get("run_id", "?")
2576
+ name = run.get("workflow", "?")
2577
+ status = run.get("status", "?")
2578
+ duration_ms = run.get("duration_ms", 0)
2579
+ lines.append(f" - {rid} [{name}] {status} {duration_ms}ms")
2580
+ return "\n".join(lines)
2581
+
2582
+
2583
+ @mcp.tool()
2584
+ def workflow_show(run_id: str) -> dict:
2585
+ """Fetch metadata for a single past workflow run.
2586
+
2587
+ Args:
2588
+ run_id: Run identifier from `workflow_history`.
2589
+
2590
+ Returns:
2591
+ The run's metadata.json contents (workflow, status, inputs, node
2592
+ summaries with timing + tokens + errors).
2593
+ """
2594
+ return run_agentwire_cmd(["workflow", "show", run_id])
2460
2595
 
2461
2596
 
2462
2597
  # =============================================================================
@@ -498,7 +498,7 @@ def dispatch_item(item: OvernightItem, config) -> bool:
498
498
 
499
499
  Returns True if dispatch succeeded.
500
500
  """
501
- from .__main__ import build_agent_command, _wait_for_agent_ready
501
+ from .__main__ import build_agent_command, _wait_for_agent_ready, inject_session_env
502
502
 
503
503
  project = item.project_path
504
504
  session = item.session
@@ -538,6 +538,9 @@ def dispatch_item(item: OvernightItem, config) -> bool:
538
538
  _log_event("dispatch_failed", item_id=item.id, error=item.error)
539
539
  return False
540
540
 
541
+ # Inject secrets via tmux set-environment (keeps keys out of `ps`)
542
+ inject_session_env(session, agent.env)
543
+
541
544
  # Inject --resume <id> --fork-session
542
545
  if item.resume_session_id:
543
546
  claude_pos = agent_cmd.rfind("claude")