agentwire-dev 1.36.0__tar.gz → 1.37.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 (389) hide show
  1. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/PKG-INFO +1 -1
  2. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/__init__.py +1 -1
  3. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/__main__.py +4 -0
  4. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/agents/tmux.py +2 -0
  5. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/history.py +2 -1
  6. agentwire_dev-1.37.0/agentwire/inbox.py +385 -0
  7. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/limits_cli.py +15 -5
  8. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/mcp_server.py +61 -0
  9. agentwire_dev-1.37.0/agentwire/msg_cli.py +163 -0
  10. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/projects.py +2 -1
  11. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/prompt_router.py +47 -0
  12. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/roles/agentwire.md +5 -1
  13. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/roles/council-member.md +1 -1
  14. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/roles/orchestrator.md +1 -0
  15. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/roles/worker.md +1 -0
  16. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/roles/worktree-mission.md +7 -1
  17. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/server.py +10 -9
  18. agentwire_dev-1.37.0/agentwire/ssh.py +100 -0
  19. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/tunnels.py +17 -8
  20. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/docs/wiki/INDEX.md +7 -0
  21. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/docs/wiki/deployment/remote-machines.md +92 -1
  22. agentwire_dev-1.37.0/docs/wiki/research/orchestration-transport-alternatives.md +176 -0
  23. agentwire_dev-1.37.0/docs/wiki/sessions/messaging.md +115 -0
  24. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/docs/wiki/sessions/prompt-routing.md +2 -0
  25. agentwire_dev-1.37.0/tests/unit/test_inbox.py +244 -0
  26. agentwire_dev-1.37.0/tests/unit/test_ssh.py +110 -0
  27. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_terminal_resize.py +6 -3
  28. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/.github/FUNDING.yml +0 -0
  29. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  30. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  31. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/.github/ISSUE_TEMPLATE/question.md +0 -0
  32. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  33. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/.github/workflows/tts-smoke.yml +0 -0
  34. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/.gitignore +0 -0
  35. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/CHANGELOG.md +0 -0
  36. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/CLA.md +0 -0
  37. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/CODE_OF_CONDUCT.md +0 -0
  38. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/CONTRIBUTING.md +0 -0
  39. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/Dockerfile.local +0 -0
  40. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/Dockerfile.runpod +0 -0
  41. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/LICENSE +0 -0
  42. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/README.md +0 -0
  43. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/RELEASING.md +0 -0
  44. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/SECURITY.md +0 -0
  45. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/SPONSORS.md +0 -0
  46. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/agents/__init__.py +0 -0
  47. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/agents/base.py +0 -0
  48. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/cached_status.py +0 -0
  49. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/channels/__init__.py +0 -0
  50. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/channels/base.py +0 -0
  51. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/channels/email.py +0 -0
  52. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/channels/quo.py +0 -0
  53. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/cli_safety.py +0 -0
  54. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/completion.py +0 -0
  55. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/config.py +0 -0
  56. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/council/__init__.py +0 -0
  57. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/council/cli.py +0 -0
  58. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/council/inbox.py +0 -0
  59. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/council/state.py +0 -0
  60. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/fetch.py +0 -0
  61. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/handoff/__init__.py +0 -0
  62. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/handoff/git_state.py +0 -0
  63. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/handoff/instructions.py +0 -0
  64. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/handoff/parser.py +0 -0
  65. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/handoff/renderer.py +0 -0
  66. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/handoff/schema.py +0 -0
  67. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/hooks/__init__.py +0 -0
  68. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/hooks/agentwire-permission.sh +0 -0
  69. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/hooks/damage-control/__init__.py +0 -0
  70. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/hooks/damage-control/audit_logger.py +0 -0
  71. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/hooks/damage-control/bash-tool-damage-control.py +0 -0
  72. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/hooks/damage-control/edit-tool-damage-control.py +0 -0
  73. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/hooks/damage-control/rules/agentwire.yaml +0 -0
  74. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/hooks/damage-control/rules/aws.yaml +0 -0
  75. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/hooks/damage-control/rules/cloud-hosting.yaml +0 -0
  76. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/hooks/damage-control/rules/containers.yaml +0 -0
  77. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/hooks/damage-control/rules/core.yaml +0 -0
  78. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/hooks/damage-control/rules/databases.yaml +0 -0
  79. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/hooks/damage-control/rules/firebase.yaml +0 -0
  80. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/hooks/damage-control/rules/gcp.yaml +0 -0
  81. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/hooks/damage-control/rules/git.yaml +0 -0
  82. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/hooks/damage-control/rules/gws.yaml +0 -0
  83. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/hooks/damage-control/rules/infrastructure.yaml +0 -0
  84. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/hooks/damage-control/rules/remote.yaml +0 -0
  85. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/hooks/damage-control/write-tool-damage-control.py +0 -0
  86. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/hooks/idle-handler.sh +0 -0
  87. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/hooks/queue-processor.sh +0 -0
  88. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/listen.py +0 -0
  89. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/locking.py +0 -0
  90. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/missions/__init__.py +0 -0
  91. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/missions/cli.py +0 -0
  92. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/missions/config.py +0 -0
  93. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/missions/dispatcher.py +0 -0
  94. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/missions/eligibility.py +0 -0
  95. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/missions/feedback_router.py +0 -0
  96. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/missions/gc.py +0 -0
  97. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/missions/github.py +0 -0
  98. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/missions/naming.py +0 -0
  99. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/missions/state.py +0 -0
  100. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/network.py +0 -0
  101. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/onboarding.py +0 -0
  102. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/overnight.py +0 -0
  103. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/pane_manager.py +0 -0
  104. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/project_config.py +0 -0
  105. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/prompts/__init__.py +0 -0
  106. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/prompts/init.md +0 -0
  107. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/prompts_cli.py +0 -0
  108. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/roles/__init__.py +0 -0
  109. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/roles/chatbot.md +0 -0
  110. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/roles/council-brain.md +0 -0
  111. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/roles/council-conscience.md +0 -0
  112. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/roles/council-critic.md +0 -0
  113. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/roles/council-devils-advocate.md +0 -0
  114. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/roles/council-gut.md +0 -0
  115. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/roles/council-historian.md +0 -0
  116. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/roles/council-orchestrator.md +0 -0
  117. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/roles/init.md +0 -0
  118. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/roles/notifications.md +0 -0
  119. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/roles/soul.md +0 -0
  120. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/roles/task-runner.md +0 -0
  121. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/roles/voice.md +0 -0
  122. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/safety/__init__.py +0 -0
  123. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/safety/_core.py +0 -0
  124. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/scheduler.py +0 -0
  125. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/scratchpad.py +0 -0
  126. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/security.py +0 -0
  127. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/services.py +0 -0
  128. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/session_ready.py +0 -0
  129. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/agentwire-Echo--black.png +0 -0
  130. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/agentwire-Echo--transparent.png +0 -0
  131. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/agentwire-Echo.png +0 -0
  132. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/agentwire-email-banner.png +0 -0
  133. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/agentwire-splash-logo-layers--agentwire-text.png +0 -0
  134. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/agentwire-splash-logo-layers--echo-claw-fg.png +0 -0
  135. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/agentwire-splash-logo-layers--echo.png +0 -0
  136. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/agentwire-splash-logo-layers--full--transparent-top.png +0 -0
  137. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/agentwire-splash-logo-layers--full-black.png +0 -0
  138. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/agentwire-splash-logo-layers--telephone-fg.png +0 -0
  139. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/agentwire-splash-logo-layers--telephone.png +0 -0
  140. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/agentwire-splash-logo-layers--transparent-top.png +0 -0
  141. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/agentwire-splash-logo-layers--transparent.png +0 -0
  142. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/agentwire-splash-logo-layers--tree.png +0 -0
  143. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/agentwire-splash-logo-layers.png +0 -0
  144. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/announcements.json +0 -0
  145. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/css/desktop.css +0 -0
  146. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/css/mobile.css +0 -0
  147. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/favicon.png +0 -0
  148. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/machines/android.jpeg +0 -0
  149. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/machines/automaton.jpeg +0 -0
  150. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/machines/bot.jpeg +0 -0
  151. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/machines/cyborg.jpeg +0 -0
  152. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/machines/droid.jpeg +0 -0
  153. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/machines/drone.jpeg +0 -0
  154. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/machines/guardian.jpeg +0 -0
  155. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/machines/mech.jpeg +0 -0
  156. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/machines/probe.jpeg +0 -0
  157. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/machines/robot.jpeg +0 -0
  158. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/machines/sentinel.jpeg +0 -0
  159. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/machines/unit.jpeg +0 -0
  160. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/projects/blob.jpeg +0 -0
  161. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/projects/cloud.jpeg +0 -0
  162. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/projects/crystal.jpeg +0 -0
  163. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/projects/cyclops.jpeg +0 -0
  164. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/projects/flame.jpeg +0 -0
  165. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/projects/fuzzy.jpeg +0 -0
  166. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/projects/horned.jpeg +0 -0
  167. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/projects/moon.jpeg +0 -0
  168. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/projects/slime.jpeg +0 -0
  169. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/projects/star.jpeg +0 -0
  170. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/projects/tentacle.jpeg +0 -0
  171. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/projects/winged.jpeg +0 -0
  172. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/sessions/bear.jpeg +0 -0
  173. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/sessions/cat.jpeg +0 -0
  174. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/sessions/crown.jpeg +0 -0
  175. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/sessions/custom/agentwire-portal.png +0 -0
  176. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/sessions/custom/agentwire-tts.png +0 -0
  177. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/sessions/custom/agentwire.png +0 -0
  178. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/sessions/deer.jpeg +0 -0
  179. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/sessions/drone.jpeg +0 -0
  180. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/sessions/eagle.jpeg +0 -0
  181. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/sessions/fox.jpeg +0 -0
  182. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/sessions/hawk.jpeg +0 -0
  183. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/sessions/horse.jpeg +0 -0
  184. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/sessions/lion.jpeg +0 -0
  185. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/sessions/rabbit.jpeg +0 -0
  186. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/sessions/robot.jpeg +0 -0
  187. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/sessions/tiger.jpeg +0 -0
  188. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/icons/sessions/wolf.jpeg +0 -0
  189. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/.gitkeep +0 -0
  190. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/announcement-modal.js +0 -0
  191. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/api.js +0 -0
  192. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/artifact-window.js +0 -0
  193. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/collage.js +0 -0
  194. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/command-palette.js +0 -0
  195. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/components/icon-picker.js +0 -0
  196. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/components/list-card.js +0 -0
  197. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/components/type-tag.js +0 -0
  198. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/dead-key-suppressor.js +0 -0
  199. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/desktop-manager.js +0 -0
  200. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/desktop.js +0 -0
  201. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/icon-manager.js +0 -0
  202. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/mobile.js +0 -0
  203. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/notification-prefs.js +0 -0
  204. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/notifications-panel.js +0 -0
  205. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/safety-shared.js +0 -0
  206. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/safety-window.js +0 -0
  207. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/scratchpad.js +0 -0
  208. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/service-classification.js +0 -0
  209. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/session-id.js +0 -0
  210. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/session-window.js +0 -0
  211. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/sidebar/artifacts-section.js +0 -0
  212. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/sidebar/config-section.js +0 -0
  213. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/sidebar/machines-section.js +0 -0
  214. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/sidebar/missions-section.js +0 -0
  215. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/sidebar/projects-section.js +0 -0
  216. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/sidebar/safety-section.js +0 -0
  217. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/sidebar/scheduler-section.js +0 -0
  218. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/sidebar/services-section.js +0 -0
  219. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/sidebar/sessions-section.js +0 -0
  220. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/sidebar.js +0 -0
  221. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/terminal-font-prefs.js +0 -0
  222. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/tile-manager.js +0 -0
  223. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/token-modal.js +0 -0
  224. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/utils/ansi.js +0 -0
  225. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/utils/auto-refresh.js +0 -0
  226. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/voice/browser-stt.js +0 -0
  227. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/voice/browser-tts.js +0 -0
  228. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/voice/jargon.js +0 -0
  229. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/voice/prompt.js +0 -0
  230. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/static/js/winbox.bundle.min.js +0 -0
  231. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/stt/__init__.py +0 -0
  232. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/stt/base.py +0 -0
  233. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/stt/cloud.py +0 -0
  234. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/stt/engine.py +0 -0
  235. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/stt/server_backend.py +0 -0
  236. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/stt/stt_server.py +0 -0
  237. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/tasks.py +0 -0
  238. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/templates/__init__.py +0 -0
  239. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/templates/base.html +0 -0
  240. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/templates/desktop.html +0 -0
  241. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/templates/email_notification.html +0 -0
  242. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/templates/handoff/show-the-story.html.j2 +0 -0
  243. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/templates/handoff/theme.css.j2 +0 -0
  244. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/templates/mobile.html +0 -0
  245. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/templates/tmux.conf +0 -0
  246. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/templating.py +0 -0
  247. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/tooldefs/aws.yaml +0 -0
  248. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/tooldefs/docker.yaml +0 -0
  249. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/tooldefs/gcp.yaml +0 -0
  250. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/tooldefs/gh.yaml +0 -0
  251. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/tooldefs/git.yaml +0 -0
  252. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/tooldefs/gws.yaml +0 -0
  253. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/tooldefs/kubectl.yaml +0 -0
  254. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/tooldefs/npm.yaml +0 -0
  255. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/tooldefs/terraform.yaml +0 -0
  256. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/tooldefs/uv.yaml +0 -0
  257. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/tts/__init__.py +0 -0
  258. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/tts/audio.py +0 -0
  259. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/tts/base.py +0 -0
  260. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/tts/engines/__init__.py +0 -0
  261. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/tts/engines/chatterbox.py +0 -0
  262. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/tts/engines/kokoro.py +0 -0
  263. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/tts/engines/qwen_base.py +0 -0
  264. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/tts/engines/qwen_custom.py +0 -0
  265. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/tts/engines/qwen_design.py +0 -0
  266. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/tts/engines/zonos.py +0 -0
  267. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/tts/local.py +0 -0
  268. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/tts/registry.py +0 -0
  269. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/tts_server.py +0 -0
  270. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/usage_limit.py +0 -0
  271. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/utils/__init__.py +0 -0
  272. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/utils/chunker.py +0 -0
  273. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/utils/file_io.py +0 -0
  274. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/utils/paths.py +0 -0
  275. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/utils/speech.py +0 -0
  276. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/utils/subprocess.py +0 -0
  277. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/validation.py +0 -0
  278. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/voiceclone.py +0 -0
  279. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/voices/darren.wav +0 -0
  280. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/voices/default.wav +0 -0
  281. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/voices/jessica.wav +0 -0
  282. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/voices/lisa.wav +0 -0
  283. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/voices/may.wav +0 -0
  284. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/agentwire/worktree.py +0 -0
  285. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/docs/decisions/obsidian-second-brain.md +0 -0
  286. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/docs/logo.png +0 -0
  287. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/docs/wiki/architecture.md +0 -0
  288. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/docs/wiki/communication/channels.md +0 -0
  289. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/docs/wiki/communication/hammerspoon.md +0 -0
  290. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/docs/wiki/communication/handoff.md +0 -0
  291. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/docs/wiki/concepts.md +0 -0
  292. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/docs/wiki/council.md +0 -0
  293. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/docs/wiki/deployment/remote-access.md +0 -0
  294. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/docs/wiki/glossary.md +0 -0
  295. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/docs/wiki/integrations/gws-google-workspace-cli.md +0 -0
  296. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/docs/wiki/internals/damage-control.md +0 -0
  297. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/docs/wiki/internals/portal.md +0 -0
  298. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/docs/wiki/internals/shell-escaping.md +0 -0
  299. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/docs/wiki/internals/troubleshooting.md +0 -0
  300. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/docs/wiki/internals/window-collage.md +0 -0
  301. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/docs/wiki/missions.md +0 -0
  302. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/docs/wiki/quickstart.md +0 -0
  303. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/docs/wiki/scheduling/scheduled-workloads.md +0 -0
  304. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/docs/wiki/security/secrets.md +0 -0
  305. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/docs/wiki/services.md +0 -0
  306. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/docs/wiki/sessions/claude-code-auto-mode.md +0 -0
  307. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/docs/wiki/sessions/pi.md +0 -0
  308. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/docs/wiki/sessions/window-sizing.md +0 -0
  309. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/docs/wiki/usage-limit-recovery.md +0 -0
  310. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/docs/wiki/voice/shim-contract.md +0 -0
  311. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/docs/wiki/voice/stt-cloud.md +0 -0
  312. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/docs/wiki/voice/stt-self-hosted.md +0 -0
  313. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/docs/wiki/voice/tts-self-hosted.md +0 -0
  314. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/pyproject.toml +0 -0
  315. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/requirements-tts.txt +0 -0
  316. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/templates/launchd/dev.agentwire.mission-dispatcher.plist +0 -0
  317. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/templates/launchd/dev.agentwire.mission-feedback-router.plist +0 -0
  318. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/templates/launchd/dev.agentwire.mission-janitor.plist +0 -0
  319. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/conftest.py +0 -0
  320. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/e2e/test_portal_ui.py +0 -0
  321. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/fixtures/sample_agentwire.yml +0 -0
  322. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/fixtures/sample_config.yaml +0 -0
  323. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/fixtures/sample_scheduler.yaml +0 -0
  324. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/integration/test_missions_concurrency.py +0 -0
  325. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/integration/test_missions_lifecycle.py +0 -0
  326. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/integration/test_scheduler_board.py +0 -0
  327. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/integration/test_server_websockets.py +0 -0
  328. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_announcements.py +0 -0
  329. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_build_agent_command.py +0 -0
  330. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_channels.py +0 -0
  331. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_cli_commands.py +0 -0
  332. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_cli_output.py +0 -0
  333. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_cli_safety.py +0 -0
  334. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_config.py +0 -0
  335. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_council_cli.py +0 -0
  336. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_council_inbox.py +0 -0
  337. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_council_state.py +0 -0
  338. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_damage_control_hooks.py +0 -0
  339. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_damage_control_sync.py +0 -0
  340. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_file_io.py +0 -0
  341. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_handoff_git_state.py +0 -0
  342. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_handoff_instructions.py +0 -0
  343. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_handoff_parser.py +0 -0
  344. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_handoff_renderer.py +0 -0
  345. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_history.py +0 -0
  346. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_hooks_install.py +0 -0
  347. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_idle_handler.py +0 -0
  348. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_locking.py +0 -0
  349. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_mcp_server.py +0 -0
  350. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_mcp_tools_args.py +0 -0
  351. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_missions_cli.py +0 -0
  352. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_missions_config.py +0 -0
  353. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_missions_dispatcher.py +0 -0
  354. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_missions_eligibility.py +0 -0
  355. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_missions_feedback_router.py +0 -0
  356. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_missions_gc.py +0 -0
  357. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_missions_github.py +0 -0
  358. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_missions_naming.py +0 -0
  359. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_missions_state.py +0 -0
  360. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_overnight_resume_flags.py +0 -0
  361. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_portal_api.py +0 -0
  362. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_project_config.py +0 -0
  363. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_prompt_router.py +0 -0
  364. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_roles.py +0 -0
  365. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_safety_disabled_rules.py +0 -0
  366. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_safety_escape_hatch.py +0 -0
  367. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_safety_kill_switch.py +0 -0
  368. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_safety_mission_worker.py +0 -0
  369. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_scheduler.py +0 -0
  370. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_scheduler_parsing.py +0 -0
  371. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_scratchpad.py +0 -0
  372. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_security.py +0 -0
  373. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_server_async.py +0 -0
  374. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_server_pure.py +0 -0
  375. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_services.py +0 -0
  376. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_session_ready.py +0 -0
  377. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_speech_tags.py +0 -0
  378. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_stt_backend.py +0 -0
  379. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_stt_cloud.py +0 -0
  380. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_stt_engine.py +0 -0
  381. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_task_cli.py +0 -0
  382. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_tasks.py +0 -0
  383. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_templating.py +0 -0
  384. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_tmux_template.py +0 -0
  385. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_tts_engine_resolution.py +0 -0
  386. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_tts_local.py +0 -0
  387. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_usage_limit.py +0 -0
  388. {agentwire_dev-1.36.0 → agentwire_dev-1.37.0}/tests/unit/test_validation.py +0 -0
  389. {agentwire_dev-1.36.0 → agentwire_dev-1.37.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.36.0
3
+ Version: 1.37.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.36.0"
3
+ __version__ = "1.37.0"
@@ -11648,6 +11648,10 @@ def main() -> int:
11648
11648
  from . import prompts_cli
11649
11649
  prompts_cli.register_prompts_parser(subparsers)
11650
11650
 
11651
+ # === msg command group (polite agent-to-agent inbox, rides the watchdog) ===
11652
+ from . import msg_cli
11653
+ msg_cli.register_msg_parser(subparsers)
11654
+
11651
11655
  # === council command group ===
11652
11656
  council_parser = subparsers.add_parser(
11653
11657
  "council",
@@ -9,6 +9,7 @@ import subprocess
9
9
  from pathlib import Path
10
10
 
11
11
  from .base import AgentBackend
12
+ from ..ssh import ssh_base_opts
12
13
 
13
14
  logger = logging.getLogger(__name__)
14
15
 
@@ -134,6 +135,7 @@ class TmuxAgent(AgentBackend):
134
135
  port = machine.get("port")
135
136
  ssh_cmd = [
136
137
  "ssh",
138
+ *ssh_base_opts(),
137
139
  "-o", "BatchMode=yes",
138
140
  "-o", "ConnectTimeout=5",
139
141
  ]
@@ -11,6 +11,7 @@ Supports both local and remote machines via SSH for distributed setups.
11
11
  import json
12
12
  from pathlib import Path
13
13
 
14
+ from .ssh import ssh_base_opts
14
15
  from .utils.file_io import load_json
15
16
  from .utils.paths import agentwire_dir
16
17
  from .utils.subprocess import run_command
@@ -124,7 +125,7 @@ def _run_ssh_command(machine: dict, command: str, timeout: int = 10) -> tuple[bo
124
125
  ssh_target = host
125
126
 
126
127
  # Build SSH command with connection timeout
127
- ssh_cmd = ["ssh", "-o", "ConnectTimeout=5", "-o", "BatchMode=yes"]
128
+ ssh_cmd = ["ssh", *ssh_base_opts(), "-o", "ConnectTimeout=5", "-o", "BatchMode=yes"]
128
129
  if port:
129
130
  ssh_cmd.extend(["-p", str(port)])
130
131
  ssh_cmd.extend([ssh_target, command])
@@ -0,0 +1,385 @@
1
+ """Polite agent-to-agent messaging — the ``agentwire msg`` inbox.
2
+
3
+ A durable, non-interrupting channel for sessions to talk amongst themselves.
4
+ Unlike ``agentwire send`` / ``session_send`` (which paste into the prompt and
5
+ press Enter *immediately* — forceful control, and the right tool when you
6
+ need it), ``msg`` drops a typed message into a per-recipient file inbox and
7
+ only injects it when the recipient's Claude Code input box is empty and the
8
+ pane is a safe delivery target. A worker reporting back can no longer clobber
9
+ a half-typed human draft.
10
+
11
+ Layout under ``~/.agentwire/inbox/``::
12
+
13
+ <session>/ # one dir per recipient session
14
+ 1718323456789-a1b2c3.json # <epoch_ms>-<short_uuid>.json (sort = order)
15
+ .lock/ # mkdir-based drain lock
16
+ dead/ # messages dropped after MAX_ATTEMPTS
17
+ .tick.lock # global flock guarding tick()
18
+
19
+ "ls is the protocol" — same pattern as Council's ``council/inbox.py``.
20
+ Sorting by filename = delivery order. Worktree session names contain ``/`` and
21
+ nest a directory level (mirrors ``usage_limit.state_path``); the tick walks
22
+ the tree and reconstructs the name from the path.
23
+
24
+ Delivery = ``safe_deliver`` guards (parked / non-agent / live-dialog refusals
25
+ + verified paste) **plus** the new ``prompt_is_empty`` collision guard.
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ import errno
31
+ import json
32
+ import os
33
+ import time
34
+ import uuid
35
+ from dataclasses import dataclass
36
+ from pathlib import Path
37
+
38
+ INBOX_ROOT = Path.home() / ".agentwire" / "inbox"
39
+ EVENTS_FILE = Path.home() / ".agentwire" / "inbox-events.jsonl"
40
+
41
+ # Typed message enum, Overstory-inspired — kept deliberately small; this is a
42
+ # mailbox, not a workflow engine.
43
+ KINDS = ("note", "done", "request", "escalation")
44
+
45
+ # Broadcast token: deliver to every live agent session except the sender.
46
+ BROADCAST_TOKEN = "@all"
47
+
48
+ # After this many failed/deferred delivery attempts a message is dead-lettered
49
+ # rather than retried forever (40 * 60s watchdog tick ≈ 40 min of a session
50
+ # being permanently busy/typed-in).
51
+ MAX_ATTEMPTS = 40
52
+
53
+ _RESERVED_DIRS = {"dead", "sent", ".lock"}
54
+
55
+
56
+ # =============================================================================
57
+ # Message model + paths
58
+ # =============================================================================
59
+
60
+
61
+ @dataclass
62
+ class Message:
63
+ id: str
64
+ sender: str # serialized as "from"
65
+ to: str
66
+ kind: str
67
+ text: str
68
+ ts: int # epoch ms
69
+ attempts: int = 0
70
+ path: Path | None = None
71
+
72
+ def to_dict(self) -> dict:
73
+ return {
74
+ "id": self.id,
75
+ "from": self.sender,
76
+ "to": self.to,
77
+ "kind": self.kind,
78
+ "text": self.text,
79
+ "ts": self.ts,
80
+ "attempts": self.attempts,
81
+ }
82
+
83
+ def render(self) -> str:
84
+ """The one-line prefix injected on delivery (mirrors [NOTIFY from …])."""
85
+ return f"[MSG from {self.sender} · {self.kind}] {self.text}"
86
+
87
+
88
+ def _now_ms() -> int:
89
+ return int(time.time() * 1000)
90
+
91
+
92
+ def _now_ns() -> int:
93
+ # Nanosecond resolution so messages enqueued within the same millisecond
94
+ # still sort by send order (the filename prefix is the ordering key; the
95
+ # uuid suffix is only a uniqueness tiebreaker, never an ordering one).
96
+ return time.time_ns()
97
+
98
+
99
+ def _short_uuid() -> str:
100
+ return uuid.uuid4().hex[:6]
101
+
102
+
103
+ def session_dir(session: str) -> Path:
104
+ return INBOX_ROOT / session
105
+
106
+
107
+ def dead_dir(session: str) -> Path:
108
+ return session_dir(session) / "dead"
109
+
110
+
111
+ def _log_event(event: str, **fields) -> None:
112
+ record = {"ts": _now_ms(), "event": event, **fields}
113
+ try:
114
+ EVENTS_FILE.parent.mkdir(parents=True, exist_ok=True)
115
+ with open(EVENTS_FILE, "a") as f:
116
+ f.write(json.dumps(record) + "\n")
117
+ except OSError:
118
+ pass
119
+
120
+
121
+ def _read_message(path: Path) -> "Message | None":
122
+ try:
123
+ data = json.loads(path.read_text())
124
+ except (OSError, json.JSONDecodeError):
125
+ return None
126
+ if not isinstance(data, dict):
127
+ return None
128
+ try:
129
+ return Message(
130
+ id=str(data["id"]),
131
+ sender=str(data.get("from", "unknown")),
132
+ to=str(data.get("to", "")),
133
+ kind=str(data.get("kind", "note")),
134
+ text=str(data.get("text", "")),
135
+ ts=int(data.get("ts", 0)),
136
+ attempts=int(data.get("attempts", 0)),
137
+ path=path,
138
+ )
139
+ except (KeyError, ValueError, TypeError):
140
+ return None
141
+
142
+
143
+ def _write_message(path: Path, msg: Message) -> None:
144
+ """Atomic write: *.tmp then rename (same dir = atomic on rename)."""
145
+ path.parent.mkdir(parents=True, exist_ok=True)
146
+ tmp = path.with_suffix(path.suffix + ".tmp")
147
+ tmp.write_text(json.dumps(msg.to_dict(), indent=2))
148
+ os.replace(tmp, path)
149
+
150
+
151
+ def pending_files(session: str) -> list[Path]:
152
+ """A session's queued message files, oldest first (excludes dead/sent)."""
153
+ sdir = session_dir(session)
154
+ if not sdir.is_dir():
155
+ return []
156
+ return sorted(sdir.glob("*.json"))
157
+
158
+
159
+ def list_messages(session: str) -> list[Message]:
160
+ return [m for m in (_read_message(f) for f in pending_files(session)) if m]
161
+
162
+
163
+ # =============================================================================
164
+ # Enqueue + broadcast
165
+ # =============================================================================
166
+
167
+
168
+ def _live_agent_sessions() -> list[str]:
169
+ """Every tmux session whose pane 0 runs an agent (Claude/pi)."""
170
+ from . import prompt_router
171
+ from .usage_limit import _tmux
172
+
173
+ try:
174
+ result = _tmux(["list-sessions", "-F", "#{session_name}"])
175
+ except Exception:
176
+ return []
177
+ if result.returncode != 0:
178
+ return []
179
+ sessions = [s for s in result.stdout.split("\n") if s.strip()]
180
+ return [s for s in sessions if prompt_router.is_agent_pane(s, 0)]
181
+
182
+
183
+ def resolve_targets(to: str, sender: "str | None") -> list[str]:
184
+ """Expand a recipient spec into concrete session names.
185
+
186
+ ``@all`` fans out to every live agent session except the sender; anything
187
+ else is a single literal session name.
188
+ """
189
+ if to == BROADCAST_TOKEN:
190
+ return [s for s in _live_agent_sessions() if s != sender]
191
+ return [to]
192
+
193
+
194
+ def enqueue(
195
+ to: str, text: str, kind: str = "note", sender: "str | None" = None
196
+ ) -> list[Message]:
197
+ """Drop a message into one or more recipient inboxes. Returns what was written."""
198
+ if kind not in KINDS:
199
+ raise ValueError(f"invalid kind: {kind!r} (expected one of {KINDS})")
200
+ if not text.strip():
201
+ raise ValueError("message text is empty")
202
+
203
+ sender = sender or "unknown"
204
+ targets = resolve_targets(to, sender)
205
+ written: list[Message] = []
206
+ for target in targets:
207
+ ns = _now_ns()
208
+ msg = Message(
209
+ id=f"{ns}-{_short_uuid()}",
210
+ sender=sender,
211
+ to=target,
212
+ kind=kind,
213
+ text=text,
214
+ ts=ns // 1_000_000, # epoch ms (schema), derived from the same clock
215
+ attempts=0,
216
+ )
217
+ path = session_dir(target) / f"{msg.id}.json"
218
+ msg.path = path
219
+ _write_message(path, msg)
220
+ _log_event(
221
+ "enqueued", id=msg.id, **{"from": sender}, to=target, kind=kind,
222
+ broadcast=(to == BROADCAST_TOKEN),
223
+ )
224
+ written.append(msg)
225
+ return written
226
+
227
+
228
+ # =============================================================================
229
+ # Drain (flush)
230
+ # =============================================================================
231
+
232
+
233
+ def _acquire_lock(session: str) -> "Path | None":
234
+ """mkdir-based per-session drain lock (mirrors queue-processor.sh)."""
235
+ lock = session_dir(session) / ".lock"
236
+ try:
237
+ lock.parent.mkdir(parents=True, exist_ok=True)
238
+ lock.mkdir()
239
+ return lock
240
+ except FileExistsError:
241
+ return None
242
+ except OSError as exc:
243
+ if exc.errno == errno.EEXIST:
244
+ return None
245
+ return None
246
+
247
+
248
+ def _release_lock(lock: "Path | None") -> None:
249
+ if lock is None:
250
+ return
251
+ try:
252
+ lock.rmdir()
253
+ except OSError:
254
+ pass
255
+
256
+
257
+ def _bump_attempts(messages: list[Message]) -> int:
258
+ """Increment attempts on each pending message; dead-letter over the cap.
259
+
260
+ Returns the number dead-lettered this pass.
261
+ """
262
+ dead = 0
263
+ for msg in messages:
264
+ if msg.path is None:
265
+ continue
266
+ msg.attempts += 1
267
+ if msg.attempts >= MAX_ATTEMPTS:
268
+ target = dead_dir(msg.to or "unknown") / msg.path.name
269
+ try:
270
+ target.parent.mkdir(parents=True, exist_ok=True)
271
+ _write_message(target, msg)
272
+ msg.path.unlink(missing_ok=True)
273
+ _log_event(
274
+ "dead_letter", id=msg.id, to=msg.to, kind=msg.kind,
275
+ attempts=msg.attempts,
276
+ )
277
+ dead += 1
278
+ except OSError:
279
+ pass
280
+ else:
281
+ try:
282
+ _write_message(msg.path, msg)
283
+ except OSError:
284
+ pass
285
+ return dead
286
+
287
+
288
+ def flush_session(session: str) -> dict:
289
+ """Attempt to drain one session's inbox now.
290
+
291
+ Delivers oldest-first, coalescing all queued messages into a single paste
292
+ (one submit) when the box is empty. On any refusal the messages stay put,
293
+ their ``attempts`` bump, and over the cap they dead-letter. Never raises.
294
+ """
295
+ from . import prompt_router
296
+
297
+ lock = _acquire_lock(session)
298
+ if lock is None:
299
+ return {"session": session, "delivered": 0, "deferred": True, "reason": "locked"}
300
+ try:
301
+ messages = list_messages(session)
302
+ if not messages:
303
+ return {"session": session, "delivered": 0, "deferred": False, "reason": "empty"}
304
+
305
+ # Collision guard FIRST (cheap, and refuses dialogs/busy too via None).
306
+ if not prompt_router.prompt_is_empty(session, 0):
307
+ dead = _bump_attempts(messages)
308
+ _log_event("deferred", to=session, count=len(messages), reason="box_not_empty")
309
+ return {
310
+ "session": session, "delivered": 0, "deferred": True,
311
+ "reason": "box_not_empty", "dead": dead,
312
+ }
313
+
314
+ rendered = "\n".join(m.render() for m in messages)
315
+ delivered, reason = prompt_router.safe_deliver(session, 0, rendered)
316
+ if not delivered:
317
+ dead = _bump_attempts(messages)
318
+ _log_event("deferred", to=session, count=len(messages), reason=reason)
319
+ return {
320
+ "session": session, "delivered": 0, "deferred": True,
321
+ "reason": reason, "dead": dead,
322
+ }
323
+
324
+ for msg in messages:
325
+ if msg.path is not None:
326
+ msg.path.unlink(missing_ok=True)
327
+ _log_event(
328
+ "delivered", to=session, count=len(messages),
329
+ kinds=[m.kind for m in messages],
330
+ )
331
+ return {
332
+ "session": session, "delivered": len(messages),
333
+ "deferred": False, "reason": "delivered",
334
+ }
335
+ except Exception as exc: # draining must never break the watchdog
336
+ _log_event("flush_failed", to=session, error=str(exc))
337
+ return {"session": session, "delivered": 0, "deferred": True, "reason": "error"}
338
+ finally:
339
+ _release_lock(lock)
340
+
341
+
342
+ def _iter_pending_sessions() -> list[str]:
343
+ """Recipient session names that currently have queued messages.
344
+
345
+ Walks the tree so worktree session names (which contain ``/`` and nest a
346
+ directory level) are reconstructed from the path; skips dead/sent/lock.
347
+ """
348
+ if not INBOX_ROOT.exists():
349
+ return []
350
+ found: set[str] = set()
351
+ for path in INBOX_ROOT.rglob("*.json"):
352
+ parts = path.relative_to(INBOX_ROOT).parts
353
+ if any(p in _RESERVED_DIRS for p in parts[:-1]):
354
+ continue
355
+ session = "/".join(parts[:-1])
356
+ if session:
357
+ found.add(session)
358
+ return sorted(found)
359
+
360
+
361
+ def tick() -> dict:
362
+ """One drain pass over every inbox with queued messages.
363
+
364
+ Rides ``agentwire limits tick`` (after the usage-limit + prompt-router
365
+ sweeps). Globally locked so a manual ``msg flush`` can't race the
366
+ watchdog. Never raises.
367
+ """
368
+ import fcntl
369
+
370
+ INBOX_ROOT.mkdir(parents=True, exist_ok=True)
371
+ lock_path = INBOX_ROOT / ".tick.lock"
372
+ with open(lock_path, "w") as lock:
373
+ try:
374
+ fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
375
+ except OSError:
376
+ return {"skipped": "tick already running"}
377
+
378
+ flushed, deferred = [], []
379
+ for session in _iter_pending_sessions():
380
+ result = flush_session(session)
381
+ if result.get("delivered"):
382
+ flushed.append(result)
383
+ elif result.get("deferred"):
384
+ deferred.append(result)
385
+ return {"flushed": flushed, "deferred": deferred}
@@ -92,16 +92,18 @@ def _fmt_local(iso: str) -> str:
92
92
  def cmd_limits_tick(args) -> int:
93
93
  """One watchdog pass (called by launchd every minute).
94
94
 
95
- Runs the usage-limit sweep FIRST, then prompt routing (#276) the
96
- ordering guarantees a usage-limit dialog is parked before the prompt
97
- sweep ever looks at the pane.
95
+ Runs the usage-limit sweep FIRST, then prompt routing (#276), then the
96
+ polite-message inbox drain (#296) — the ordering guarantees a usage-limit
97
+ dialog is parked before either sweep looks at the pane, and that the
98
+ inbox only ever delivers to panes the prompt sweep already cleared.
98
99
  """
99
- from agentwire import prompt_router
100
+ from agentwire import inbox, prompt_router
100
101
 
101
102
  result = usage_limit.tick()
102
103
  prompts = prompt_router.tick()
104
+ messages = inbox.tick()
103
105
  if getattr(args, "json", False):
104
- print(json.dumps({**result, "prompts": prompts}))
106
+ print(json.dumps({**result, "prompts": prompts, "messages": messages}))
105
107
  return 0
106
108
  if result.get("skipped"):
107
109
  print(result["skipped"])
@@ -122,6 +124,14 @@ def cmd_limits_tick(args) -> int:
122
124
  if deferred:
123
125
  print("prompts deferred: " + ", ".join(
124
126
  f"{e['session']}.{e['pane']}" for e in deferred))
127
+ flushed = messages.get("flushed") or []
128
+ msg_deferred = messages.get("deferred") or []
129
+ if flushed:
130
+ print("messages delivered: " + ", ".join(
131
+ f"{e['session']}×{e['delivered']}" for e in flushed))
132
+ if msg_deferred:
133
+ print("messages deferred: " + ", ".join(
134
+ f"{e['session']}({e['reason']})" for e in msg_deferred))
125
135
  return 0
126
136
 
127
137
 
@@ -380,6 +380,67 @@ def session_send(session: str, message: str) -> str:
380
380
  return f"Failed to send message: {data.get('error', 'Unknown error')}"
381
381
 
382
382
 
383
+ @mcp.tool()
384
+ def msg_send(to: str, text: str, kind: str = "note") -> str:
385
+ """Send a POLITE, non-interrupting message to another session's inbox.
386
+
387
+ Use this for routine peer updates that should NOT interrupt — a worker
388
+ reporting "PR drafted", an orchestrator nudging a sibling. The message
389
+ drops into a durable inbox and only injects when the recipient's input box
390
+ is empty and the pane is safe, so it can never clobber a human who is
391
+ mid-typing. Delivery is at the next safe boundary (≤60s), not instant.
392
+
393
+ Prefer `session_send` ONLY when you must forcibly drive a session right
394
+ now (it pastes + Enter immediately, overwriting any uncommitted draft).
395
+
396
+ Args:
397
+ to: Recipient session name, or "@all" to broadcast to every live
398
+ agent session except yourself.
399
+ text: The message body.
400
+ kind: One of note (default), done, request, escalation.
401
+
402
+ Returns:
403
+ Confirmation of which sessions were queued, or an error.
404
+ """
405
+ caller = get_caller_session()
406
+ args = ["msg", "send", "--to", to, "--kind", kind]
407
+ if caller:
408
+ args += ["--from", caller]
409
+ args.append(text)
410
+ data = run_agentwire_cmd(args)
411
+ if data.get("success"):
412
+ recipients = data.get("recipients") or []
413
+ if not recipients:
414
+ return f"No live recipients for '{to}'."
415
+ return f"Queued {kind} → {', '.join(recipients)} (delivers when their box is clear)."
416
+ return f"Failed to queue message: {data.get('error', 'Unknown error')}"
417
+
418
+
419
+ @mcp.tool()
420
+ def msg_inbox(session: str | None = None) -> str:
421
+ """Peek a session's pending polite messages (does not drain them).
422
+
423
+ Args:
424
+ session: Session name (default: the calling session).
425
+
426
+ Returns:
427
+ The pending messages, or a note that the inbox is empty.
428
+ """
429
+ args = ["msg", "inbox"]
430
+ if session:
431
+ args += ["-s", session]
432
+ data = run_agentwire_cmd(args)
433
+ if not data.get("success"):
434
+ return f"Failed to read inbox: {data.get('error', 'Unknown error')}"
435
+ pending = data.get("pending") or []
436
+ if not pending:
437
+ return f"Inbox empty for {data.get('session', session or 'this session')}."
438
+ lines = [f"{len(pending)} pending for {data.get('session')}:"]
439
+ for m in pending:
440
+ lines.append(f" [{m.get('kind')}] from {m.get('from')}: {m.get('text')}")
441
+ return "\n".join(lines)
442
+
443
+
383
444
  @mcp.tool()
384
445
  def session_output(session: str, lines: int = 50) -> str:
385
446
  """Capture output from a session.