agentwire-dev 1.50.0__tar.gz → 1.51.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 (456) hide show
  1. agentwire_dev-1.51.0/.agentwire.yml.example +14 -0
  2. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/PKG-INFO +5 -1
  3. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/README.md +4 -0
  4. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/__init__.py +1 -1
  5. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/hooks/idle-handler.sh +11 -4
  6. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/inbox.py +226 -35
  7. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/mcp_msg.py +35 -5
  8. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/msg_cli.py +42 -3
  9. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/notify_cli.py +27 -6
  10. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/onboarding.py +1 -0
  11. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/pane_cli.py +9 -0
  12. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/project_config.py +21 -12
  13. agentwire_dev-1.51.0/agentwire/roles/contributor.md +83 -0
  14. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/services.py +36 -0
  15. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/session_cli.py +28 -13
  16. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/session_ready.py +59 -2
  17. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/system_cli.py +19 -4
  18. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/quickstart.md +10 -0
  19. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/sessions/messaging.md +43 -3
  20. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/sessions/prompt-routing.md +12 -0
  21. agentwire_dev-1.51.0/tests/integration/test_send_verified_tmux.py +186 -0
  22. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_inbox.py +207 -1
  23. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_project_config.py +30 -0
  24. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_roles.py +1 -1
  25. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_session_ready.py +38 -0
  26. agentwire_dev-1.51.0/tests/unit/test_shared_dir_guard.py +44 -0
  27. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/.claude/commands/handoff.md +0 -0
  28. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/.claude/skills/wiki/SKILL.md +0 -0
  29. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/.github/FUNDING.yml +0 -0
  30. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  31. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  32. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/.github/ISSUE_TEMPLATE/question.md +0 -0
  33. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  34. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/.github/workflows/pytest.yml +0 -0
  35. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/.github/workflows/ruff.yml +0 -0
  36. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/.github/workflows/security.yml +0 -0
  37. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/.github/workflows/tts-smoke.yml +0 -0
  38. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/.gitignore +0 -0
  39. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/AGENTS.md +0 -0
  40. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/CODE_OF_CONDUCT.md +0 -0
  41. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/CONTRIBUTING.md +0 -0
  42. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/Dockerfile.local +0 -0
  43. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/LICENSE +0 -0
  44. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/RELEASING.md +0 -0
  45. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/SECURITY.md +0 -0
  46. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/SPONSORS.md +0 -0
  47. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/__main__.py +0 -0
  48. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/agents/__init__.py +0 -0
  49. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/agents/base.py +0 -0
  50. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/agents/tmux.py +0 -0
  51. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/cached_status.py +0 -0
  52. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/channels/__init__.py +0 -0
  53. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/channels/base.py +0 -0
  54. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/channels/email.py +0 -0
  55. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/channels/push.py +0 -0
  56. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/channels/quo.py +0 -0
  57. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/channels_cli.py +0 -0
  58. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/completion.py +0 -0
  59. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/config.py +0 -0
  60. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/core.py +0 -0
  61. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/council/__init__.py +0 -0
  62. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/council/cli.py +0 -0
  63. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/council/inbox.py +0 -0
  64. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/council/state.py +0 -0
  65. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/council/view.py +0 -0
  66. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/devices.py +0 -0
  67. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/diff_cli.py +0 -0
  68. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/doctor_cli.py +0 -0
  69. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/doctor_voice.py +0 -0
  70. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/ensure_cli.py +0 -0
  71. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/fetch.py +0 -0
  72. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/handoff/__init__.py +0 -0
  73. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/handoff/git_state.py +0 -0
  74. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/handoff/instructions.py +0 -0
  75. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/handoff/parser.py +0 -0
  76. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/handoff/renderer.py +0 -0
  77. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/handoff/schema.py +0 -0
  78. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/handoff_cli.py +0 -0
  79. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/history.py +0 -0
  80. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/history_cli.py +0 -0
  81. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/hooks/__init__.py +0 -0
  82. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/hooks/agentwire-permission.sh +0 -0
  83. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/hooks/damage-control/__init__.py +0 -0
  84. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/hooks/damage-control/audit_logger.py +0 -0
  85. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/hooks/damage-control/bash-tool-damage-control.py +0 -0
  86. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/hooks/damage-control/edit-tool-damage-control.py +0 -0
  87. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/hooks/damage-control/mcp-tool-damage-control.py +0 -0
  88. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/hooks/damage-control/read-tool-damage-control.py +0 -0
  89. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/hooks/damage-control/rules/agentwire.yaml +0 -0
  90. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/hooks/damage-control/rules/aws.yaml +0 -0
  91. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/hooks/damage-control/rules/cloud-hosting.yaml +0 -0
  92. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/hooks/damage-control/rules/containers.yaml +0 -0
  93. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/hooks/damage-control/rules/control-plane.yaml +0 -0
  94. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/hooks/damage-control/rules/core.yaml +0 -0
  95. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/hooks/damage-control/rules/databases.yaml +0 -0
  96. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/hooks/damage-control/rules/firebase.yaml +0 -0
  97. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/hooks/damage-control/rules/gcp.yaml +0 -0
  98. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/hooks/damage-control/rules/git.yaml +0 -0
  99. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/hooks/damage-control/rules/gws.yaml +0 -0
  100. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/hooks/damage-control/rules/infrastructure.yaml +0 -0
  101. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/hooks/damage-control/rules/outbound.yaml +0 -0
  102. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/hooks/damage-control/rules/publish.yaml +0 -0
  103. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/hooks/damage-control/rules/remote.yaml +0 -0
  104. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/hooks/damage-control/write-tool-damage-control.py +0 -0
  105. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/hooks/queue-processor.sh +0 -0
  106. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/hooks_cli.py +0 -0
  107. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/limits_cli.py +0 -0
  108. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/listen.py +0 -0
  109. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/locking.py +0 -0
  110. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/machine_cli.py +0 -0
  111. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/mcp_channels.py +0 -0
  112. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/mcp_cli.py +0 -0
  113. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/mcp_core.py +0 -0
  114. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/mcp_council.py +0 -0
  115. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/mcp_desktop.py +0 -0
  116. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/mcp_handoff.py +0 -0
  117. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/mcp_history.py +0 -0
  118. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/mcp_listen.py +0 -0
  119. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/mcp_lock.py +0 -0
  120. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/mcp_machine.py +0 -0
  121. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/mcp_notify.py +0 -0
  122. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/mcp_pane.py +0 -0
  123. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/mcp_projects.py +0 -0
  124. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/mcp_scheduler.py +0 -0
  125. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/mcp_scratchpad.py +0 -0
  126. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/mcp_server.py +0 -0
  127. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/mcp_services.py +0 -0
  128. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/mcp_session.py +0 -0
  129. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/mcp_status.py +0 -0
  130. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/mcp_task.py +0 -0
  131. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/mcp_tunnels.py +0 -0
  132. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/mcp_voice.py +0 -0
  133. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/mcp_voiceclone.py +0 -0
  134. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/mcp_wiki.py +0 -0
  135. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/mcp_worktree.py +0 -0
  136. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/network.py +0 -0
  137. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/pane_manager.py +0 -0
  138. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/portal_cli.py +0 -0
  139. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/projects.py +0 -0
  140. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/prompt_router.py +0 -0
  141. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/prompts_cli.py +0 -0
  142. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/push_cli.py +0 -0
  143. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/push_store.py +0 -0
  144. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/repo_cli.py +0 -0
  145. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/research.py +0 -0
  146. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/roles/__init__.py +0 -0
  147. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/roles/agentwire.md +0 -0
  148. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/roles/anchor.md +0 -0
  149. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/roles/chatbot.md +0 -0
  150. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/roles/correspondent.md +0 -0
  151. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/roles/council-brain.md +0 -0
  152. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/roles/council-conscience.md +0 -0
  153. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/roles/council-critic.md +0 -0
  154. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/roles/council-devils-advocate.md +0 -0
  155. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/roles/council-gut.md +0 -0
  156. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/roles/council-historian.md +0 -0
  157. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/roles/council-member.md +0 -0
  158. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/roles/council-orchestrator.md +0 -0
  159. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/roles/init.md +0 -0
  160. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/roles/notifications.md +0 -0
  161. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/roles/orchestrator.md +0 -0
  162. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/roles/soul.md +0 -0
  163. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/roles/task-runner.md +0 -0
  164. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/roles/voice.md +0 -0
  165. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/roles/worker.md +0 -0
  166. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/roles/worktree-session.md +0 -0
  167. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/roles_cli.py +0 -0
  168. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/routes/__init__.py +0 -0
  169. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/routes/artifacts.py +0 -0
  170. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/routes/config.py +0 -0
  171. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/routes/council.py +0 -0
  172. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/routes/desktop.py +0 -0
  173. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/routes/history.py +0 -0
  174. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/routes/machines.py +0 -0
  175. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/routes/notify.py +0 -0
  176. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/routes/permission.py +0 -0
  177. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/routes/projects.py +0 -0
  178. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/routes/push.py +0 -0
  179. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/routes/safety.py +0 -0
  180. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/routes/scheduler.py +0 -0
  181. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/routes/scratchpad.py +0 -0
  182. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/routes/sessions.py +0 -0
  183. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/routes/sessions_admin.py +0 -0
  184. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/routes/voice.py +0 -0
  185. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/safety/__init__.py +0 -0
  186. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/safety/_core.py +0 -0
  187. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/safety_cli.py +0 -0
  188. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/safety_commands.py +0 -0
  189. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/scheduler.py +0 -0
  190. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/scheduler_cli.py +0 -0
  191. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/scratchpad.py +0 -0
  192. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/security.py +0 -0
  193. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/send_cli.py +0 -0
  194. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/server.py +0 -0
  195. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/session_context.py +0 -0
  196. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/ssh.py +0 -0
  197. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/agentwire-splash-logo-layers--agentwire-text.png +0 -0
  198. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/agentwire-splash-logo-layers--echo-claw-fg.png +0 -0
  199. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/agentwire-splash-logo-layers--echo.png +0 -0
  200. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/agentwire-splash-logo-layers--telephone.png +0 -0
  201. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/agentwire-splash-logo-layers--tree.png +0 -0
  202. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/announcements.json +0 -0
  203. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/css/desktop.css +0 -0
  204. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/css/mobile.css +0 -0
  205. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/favicon.png +0 -0
  206. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/icons/sessions/bear.webp +0 -0
  207. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/icons/sessions/cat.webp +0 -0
  208. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/icons/sessions/crown.webp +0 -0
  209. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/icons/sessions/custom/agentwire-portal.webp +0 -0
  210. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/icons/sessions/custom/agentwire-tts.webp +0 -0
  211. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/icons/sessions/custom/agentwire.webp +0 -0
  212. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/icons/sessions/deer.webp +0 -0
  213. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/icons/sessions/drone.webp +0 -0
  214. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/icons/sessions/eagle.webp +0 -0
  215. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/icons/sessions/fox.webp +0 -0
  216. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/icons/sessions/hawk.webp +0 -0
  217. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/icons/sessions/horse.webp +0 -0
  218. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/icons/sessions/lion.webp +0 -0
  219. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/icons/sessions/rabbit.webp +0 -0
  220. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/icons/sessions/robot.webp +0 -0
  221. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/icons/sessions/tiger.webp +0 -0
  222. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/icons/sessions/wolf.webp +0 -0
  223. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/img/icon-192.png +0 -0
  224. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/img/icon-512.png +0 -0
  225. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/.gitkeep +0 -0
  226. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/announcement-modal.js +0 -0
  227. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/api.js +0 -0
  228. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/artifact-window.js +0 -0
  229. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/collage.js +0 -0
  230. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/command-palette.js +0 -0
  231. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/council-window.js +0 -0
  232. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/dead-key-suppressor.js +0 -0
  233. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/desktop-manager.js +0 -0
  234. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/desktop.js +0 -0
  235. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/help-modal.js +0 -0
  236. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/icon-manager.js +0 -0
  237. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/mobile.js +0 -0
  238. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/notification-prefs.js +0 -0
  239. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/notifications-panel.js +0 -0
  240. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/push-client.js +0 -0
  241. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/review-window.js +0 -0
  242. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/safety-shared.js +0 -0
  243. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/safety-window.js +0 -0
  244. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/scratchpad.js +0 -0
  245. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/service-classification.js +0 -0
  246. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/session-id.js +0 -0
  247. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/session-window.js +0 -0
  248. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/shortcuts.js +0 -0
  249. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/sidebar/artifacts-section.js +0 -0
  250. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/sidebar/config-section.js +0 -0
  251. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/sidebar/council-section.js +0 -0
  252. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/sidebar/machines-section.js +0 -0
  253. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/sidebar/projects-section.js +0 -0
  254. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/sidebar/safety-section.js +0 -0
  255. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/sidebar/scheduler-section.js +0 -0
  256. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/sidebar/services-section.js +0 -0
  257. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/sidebar/sessions-section.js +0 -0
  258. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/sidebar.js +0 -0
  259. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/terminal-font-prefs.js +0 -0
  260. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/tile-manager.js +0 -0
  261. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/toast.js +0 -0
  262. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/token-modal.js +0 -0
  263. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/utils/ansi.js +0 -0
  264. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/utils/swipe.js +0 -0
  265. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/voice/autosend-prefs.js +0 -0
  266. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/voice/browser-stt.js +0 -0
  267. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/voice/browser-tts.js +0 -0
  268. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/voice/jargon.js +0 -0
  269. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/voice/prompt.js +0 -0
  270. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/js/winbox.bundle.min.js +0 -0
  271. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/manifest.webmanifest +0 -0
  272. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/static/service-worker.js +0 -0
  273. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/stt/__init__.py +0 -0
  274. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/stt/base.py +0 -0
  275. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/stt/cloud.py +0 -0
  276. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/stt/engine.py +0 -0
  277. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/stt/local.py +0 -0
  278. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/stt/server_backend.py +0 -0
  279. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/stt/stt_server.py +0 -0
  280. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/tasks.py +0 -0
  281. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/templates/__init__.py +0 -0
  282. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/templates/base.html +0 -0
  283. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/templates/desktop.html +0 -0
  284. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/templates/email_notification.html +0 -0
  285. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/templates/handoff/show-the-story.html.j2 +0 -0
  286. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/templates/handoff/theme.css.j2 +0 -0
  287. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/templates/mobile.html +0 -0
  288. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/templates/pair.html +0 -0
  289. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/templates/tmux.conf +0 -0
  290. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/templating.py +0 -0
  291. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/tooldefs/aws.yaml +0 -0
  292. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/tooldefs/docker.yaml +0 -0
  293. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/tooldefs/gcp.yaml +0 -0
  294. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/tooldefs/gh.yaml +0 -0
  295. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/tooldefs/git.yaml +0 -0
  296. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/tooldefs/gws.yaml +0 -0
  297. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/tooldefs/kubectl.yaml +0 -0
  298. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/tooldefs/npm.yaml +0 -0
  299. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/tooldefs/terraform.yaml +0 -0
  300. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/tooldefs/uv.yaml +0 -0
  301. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/tts/__init__.py +0 -0
  302. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/tts/audio.py +0 -0
  303. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/tts/base.py +0 -0
  304. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/tts/engines/__init__.py +0 -0
  305. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/tts/engines/chatterbox.py +0 -0
  306. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/tts/engines/kokoro.py +0 -0
  307. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/tts/engines/zonos.py +0 -0
  308. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/tts/kokoro_server.py +0 -0
  309. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/tts/local.py +0 -0
  310. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/tts/registry.py +0 -0
  311. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/tts_cli.py +0 -0
  312. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/tts_server.py +0 -0
  313. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/tunnels.py +0 -0
  314. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/tunnels_cli.py +0 -0
  315. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/usage_limit.py +0 -0
  316. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/utils/__init__.py +0 -0
  317. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/utils/chunker.py +0 -0
  318. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/utils/event_log.py +0 -0
  319. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/utils/file_io.py +0 -0
  320. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/utils/paths.py +0 -0
  321. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/utils/speech.py +0 -0
  322. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/utils/subprocess.py +0 -0
  323. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/validation.py +0 -0
  324. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/voice_status.py +0 -0
  325. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/voiceclone.py +0 -0
  326. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/voices/darren.wav +0 -0
  327. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/voices/default.wav +0 -0
  328. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/voices/jessica.wav +0 -0
  329. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/voices/lisa.wav +0 -0
  330. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/voices/may.wav +0 -0
  331. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/wiki.py +0 -0
  332. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/wiki_audit.py +0 -0
  333. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/wiki_cli.py +0 -0
  334. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/worktree.py +0 -0
  335. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/agentwire/worktree_registry.py +0 -0
  336. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/decisions/obsidian-second-brain.md +0 -0
  337. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/logo.png +0 -0
  338. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/INDEX.md +0 -0
  339. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/architecture.md +0 -0
  340. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/briefing-mode.md +0 -0
  341. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/communication/channels.md +0 -0
  342. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/communication/hammerspoon.md +0 -0
  343. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/communication/handoff.md +0 -0
  344. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/concepts.md +0 -0
  345. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/council.md +0 -0
  346. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/deployment/remote-access.md +0 -0
  347. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/deployment/remote-machines.md +0 -0
  348. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/glossary.md +0 -0
  349. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/integrations/gws-google-workspace-cli.md +0 -0
  350. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/internals/damage-control.md +0 -0
  351. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/internals/parallel-refactor.md +0 -0
  352. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/internals/portal.md +0 -0
  353. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/internals/shell-escaping.md +0 -0
  354. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/internals/troubleshooting.md +0 -0
  355. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/internals/window-collage.md +0 -0
  356. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/research/briefing-mode-feasibility.md +0 -0
  357. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/research/orchestration-transport-alternatives.md +0 -0
  358. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/scheduling/scheduled-workloads.md +0 -0
  359. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/security/damage-control-hardening.md +0 -0
  360. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/security/pip-audit.md +0 -0
  361. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/security/remote-access-hardening.md +0 -0
  362. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/security/secrets.md +0 -0
  363. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/services.md +0 -0
  364. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/sessions/claude-code-auto-mode.md +0 -0
  365. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/sessions/pi.md +0 -0
  366. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/sessions/window-sizing.md +0 -0
  367. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/sessions/worktree-sessions.md +0 -0
  368. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/usage-limit-recovery.md +0 -0
  369. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/voice/shim-contract.md +0 -0
  370. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/voice/stt-cloud.md +0 -0
  371. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/voice/stt-self-hosted.md +0 -0
  372. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/docs/wiki/voice/tts-self-hosted.md +0 -0
  373. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/pyproject.toml +0 -0
  374. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/conftest.py +0 -0
  375. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/e2e/test_portal_ui.py +0 -0
  376. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/fixtures/sample_agentwire.yml +0 -0
  377. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/fixtures/sample_config.yaml +0 -0
  378. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/fixtures/sample_scheduler.yaml +0 -0
  379. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/integration/test_council_portal.py +0 -0
  380. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/integration/test_scheduler_board.py +0 -0
  381. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/integration/test_server_websockets.py +0 -0
  382. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/integration/test_worktree_cmd.py +0 -0
  383. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_announcements.py +0 -0
  384. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_build_agent_command.py +0 -0
  385. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_channels.py +0 -0
  386. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_cli_commands.py +0 -0
  387. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_cli_output.py +0 -0
  388. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_cli_smoke.py +0 -0
  389. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_config.py +0 -0
  390. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_control_plane_protection.py +0 -0
  391. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_council_cli.py +0 -0
  392. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_council_inbox.py +0 -0
  393. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_council_state.py +0 -0
  394. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_council_view.py +0 -0
  395. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_damage_control_bypass.py +0 -0
  396. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_damage_control_hooks.py +0 -0
  397. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_damage_control_sync.py +0 -0
  398. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_devices.py +0 -0
  399. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_diff_cli.py +0 -0
  400. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_doctor_voice.py +0 -0
  401. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_event_log.py +0 -0
  402. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_file_io.py +0 -0
  403. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_handoff_git_state.py +0 -0
  404. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_handoff_instructions.py +0 -0
  405. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_handoff_parser.py +0 -0
  406. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_handoff_renderer.py +0 -0
  407. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_history.py +0 -0
  408. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_hooks_install.py +0 -0
  409. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_idle_handler.py +0 -0
  410. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_idle_nag.py +0 -0
  411. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_init_command.py +0 -0
  412. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_limits_tick_isolation.py +0 -0
  413. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_locking.py +0 -0
  414. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_mcp_server.py +0 -0
  415. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_mcp_tools_args.py +0 -0
  416. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_msg_cli.py +0 -0
  417. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_portal_api.py +0 -0
  418. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_prompt_router.py +0 -0
  419. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_push.py +0 -0
  420. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_rebuild_guard.py +0 -0
  421. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_repo_cli.py +0 -0
  422. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_routes_snapshot.py +0 -0
  423. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_safety_commands.py +0 -0
  424. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_safety_disabled_rules.py +0 -0
  425. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_safety_escape_hatch.py +0 -0
  426. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_safety_heal.py +0 -0
  427. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_safety_kill_switch.py +0 -0
  428. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_scheduler.py +0 -0
  429. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_scheduler_parsing.py +0 -0
  430. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_scratchpad.py +0 -0
  431. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_security.py +0 -0
  432. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_server_async.py +0 -0
  433. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_server_pure.py +0 -0
  434. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_services.py +0 -0
  435. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_session_context.py +0 -0
  436. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_skill_install.py +0 -0
  437. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_speech_tags.py +0 -0
  438. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_ssh.py +0 -0
  439. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_stt_backend.py +0 -0
  440. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_stt_cloud.py +0 -0
  441. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_stt_engine.py +0 -0
  442. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_task_cli.py +0 -0
  443. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_tasks.py +0 -0
  444. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_templating.py +0 -0
  445. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_terminal_resize.py +0 -0
  446. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_tmux_template.py +0 -0
  447. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_tts_engine_resolution.py +0 -0
  448. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_tts_local.py +0 -0
  449. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_tunnels_probe.py +0 -0
  450. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_usage_limit.py +0 -0
  451. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_validation.py +0 -0
  452. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_voice_status.py +0 -0
  453. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_wiki.py +0 -0
  454. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_wiki_audit.py +0 -0
  455. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_worktree.py +0 -0
  456. {agentwire_dev-1.50.0 → agentwire_dev-1.51.0}/tests/unit/test_worktree_registry.py +0 -0
@@ -0,0 +1,14 @@
1
+ # agentwire-dev project config — committed template (#620).
2
+ #
3
+ # This `.agentwire.yml.example` is the default config a fresh clone uses out of
4
+ # the box: `agentwire new` here spawns a contributor session (repo-aware
5
+ # onboarding + issue-filing + fork-based PR coaching).
6
+ #
7
+ # To override locally, create a real `.agentwire.yml` next to this file. It is
8
+ # gitignored, so it stays personal and never ships — and it WINS over this
9
+ # template (a live `.agentwire.yml` is resolved before the `.example` fallback).
10
+ # The repo owner uses this to layer an untracked owner-override role:
11
+ # roles: [contributor, owner-override]
12
+ type: claude-bypass
13
+ roles:
14
+ - contributor
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentwire-dev
3
- Version: 1.50.0
3
+ Version: 1.51.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
@@ -345,6 +345,10 @@ agentwire init
345
345
  # Run
346
346
  agentwire portal start
347
347
  # Open http://127.0.0.1:8765 in Chrome — your cockpit is live (voice works immediately too)
348
+
349
+ # New here? Start a guided helper session — it walks you through setup,
350
+ # wires up your projects, explains the system, and helps you file issues or fork.
351
+ agentwire dev
348
352
  ```
349
353
 
350
354
  **Requirements:** Python 3.10+, tmux, ffmpeg, Claude Code
@@ -82,6 +82,10 @@ agentwire init
82
82
  # Run
83
83
  agentwire portal start
84
84
  # Open http://127.0.0.1:8765 in Chrome — your cockpit is live (voice works immediately too)
85
+
86
+ # New here? Start a guided helper session — it walks you through setup,
87
+ # wires up your projects, explains the system, and helps you file issues or fork.
88
+ agentwire dev
85
89
  ```
86
90
 
87
91
  **Requirements:** Python 3.10+, tmux, ffmpeg, Claude Code
@@ -1,3 +1,3 @@
1
1
  """AgentWire - Multi-session voice web interface for AI coding agents."""
2
2
 
3
- __version__ = "1.50.0"
3
+ __version__ = "1.51.0"
@@ -370,10 +370,17 @@ complete | incomplete | error
370
370
  echo "[$(date -Iseconds)] TASK-ORPHAN: killing tmux session" >> "$dlog"
371
371
  tmux kill-session -t "$tmux_session" 2>/dev/null &
372
372
  ) &
373
- elif [[ -n "$parent_session" ]]; then
374
- # Orchestrator pane with parent: notify parent
375
- message="${session_name} is idle"
376
- $AGENTWIRE notify-parent -q --to "$parent_session" "$message" 2>/dev/null &
373
+ else
374
+ # Pane-0 session went idle: tell its parent. Resolution lives inside
375
+ # notify-parent (prompt_router.resolve_parent) so all three precedence
376
+ # sources are honored worker pane → pane 0, creator recorded at
377
+ # `agentwire new` time (session metadata), then `.agentwire.yml parent:`.
378
+ # The old code passed only the .agentwire.yml value and skipped the call
379
+ # entirely when it was empty, so worktree / `agentwire new` children
380
+ # (whose parent lives in metadata, not config) never notified anyone.
381
+ # TMUX_PANE is inherited, so the CLI knows which session this is; a
382
+ # top-level session resolves to no parent and this is a harmless no-op.
383
+ $AGENTWIRE notify-parent --on-idle -q "is idle and done working" 2>/dev/null &
377
384
  fi
378
385
  fi
379
386
  fi
@@ -114,9 +114,23 @@ class Message:
114
114
  "ref": self.ref,
115
115
  }
116
116
 
117
+ def short_id(self) -> str:
118
+ """The 6-char uuid tail of ``id`` (the ``{epoch_ns}-{uuid6}`` suffix)."""
119
+ return self.id.rsplit("-", 1)[-1]
120
+
117
121
  def render(self) -> str:
118
- """The one-line prefix injected on delivery (mirrors [NOTIFY from …])."""
119
- return f"[MSG from {self.sender} · {self.kind}] {self.text}"
122
+ """The one-line message injected on delivery (mirrors [NOTIFY from …]).
123
+
124
+ The trailing ``⟨#id6⟩`` token makes every delivered line UNIQUE on the
125
+ recipient's screen (#621). Idempotent-redelivery dedup matches the full
126
+ rendered line on scrollback; without a unique tail a shorter message
127
+ whose text is a prefix of a longer same-sender/kind one (or two
128
+ identical-text report-backs) would substring-collide and be consumed
129
+ without delivery. The token is appended at the END so the prefix-based
130
+ landing checks (``derive_check_fragment`` / ``message_visible``) are
131
+ unchanged.
132
+ """
133
+ return f"[MSG from {self.sender} · {self.kind}] {self.text} ⟨#{self.short_id()}⟩"
120
134
 
121
135
 
122
136
  def _now_ms() -> int:
@@ -293,6 +307,111 @@ def purge_dead(session: "str | None" = None, before_ms: "int | None" = None) ->
293
307
  return removed
294
308
 
295
309
 
310
+ def purge_pending(session: str) -> int:
311
+ """Drop a session's *pending* (undelivered) messages; return how many.
312
+
313
+ The self-heal escape hatch (#621): when a recipient is wedged into a
314
+ redelivery loop, the only prior recovery was hand-moving JSON files — which
315
+ the recipient's own Bash hook blocks (``rm``). This drops the pending queue
316
+ outright, no empty-box gate, no delivery. Passive (``ingest/``) and dead
317
+ (``dead/``) messages are untouched — this is strictly the active drain queue.
318
+ """
319
+ # Serialize against an in-flight flush_session via the per-session drain lock
320
+ # so we don't yank a file mid-delivery. If a flush holds the lock we still
321
+ # proceed (the operator wants the queue gone) — unlinking under it is benign
322
+ # because flush copies messages into memory first and its own unlink is
323
+ # missing_ok.
324
+ lock = _acquire_lock(session)
325
+ try:
326
+ paths = pending_files(session)
327
+ removed = 0
328
+ for path in paths:
329
+ try:
330
+ path.unlink()
331
+ removed += 1
332
+ except OSError:
333
+ pass
334
+ if removed:
335
+ _log_event("purged_pending", session=session, count=removed)
336
+ return removed
337
+ finally:
338
+ _release_lock(lock)
339
+
340
+
341
+ def gc_sender(sender: str) -> dict:
342
+ """Garbage-collect an exited sender's still-pending outbound (#621).
343
+
344
+ Messages live keyed by *recipient*, so when a worktree/session exits nothing
345
+ reaps the report-backs it left undelivered across every inbox — they
346
+ accumulate. This scans all pending queues for that sender and clears them:
347
+ load-bearing kinds (``done``/``request``/``escalation``) are dead-lettered
348
+ (which escalates via the owner-email path so the loss is never silent); the
349
+ rest are dropped. Passive (``ingest``) messages are never auto-delivered, so
350
+ they're left for the recipient to pull. Returns ``{dead, dropped}`` counts.
351
+ """
352
+ dead = dropped = 0
353
+ if not INBOX_ROOT.exists():
354
+ return {"dead": dead, "dropped": dropped}
355
+
356
+ # Group this sender's pending files by recipient so each inbox is mutated
357
+ # under its per-session drain lock — serializing against an in-flight
358
+ # flush_session. Without it a kill landing mid-delivery could dead-letter +
359
+ # email "never delivered" for a message that WAS just delivered.
360
+ by_recipient: dict[str, list[Path]] = {}
361
+ for path in INBOX_ROOT.rglob("*.json"):
362
+ parts = path.relative_to(INBOX_ROOT).parts
363
+ if any(p in _RESERVED_DIRS for p in parts[:-1]):
364
+ continue # skip dead/ sent/ ingest/ .lock/
365
+ msg = _read_message(path)
366
+ if msg is None or msg.sender != sender:
367
+ continue
368
+ recipient = "/".join(parts[:-1])
369
+ if recipient:
370
+ by_recipient.setdefault(recipient, []).append(path)
371
+
372
+ for recipient, paths in by_recipient.items():
373
+ lock = _acquire_lock(recipient)
374
+ if lock is None:
375
+ # A flush is draining this inbox right now — its messages are being
376
+ # delivered, not lost. Skip GC for this recipient this round.
377
+ continue
378
+ try:
379
+ for path in paths:
380
+ if not path.exists():
381
+ continue # delivered + unlinked just before we locked
382
+ msg = _read_message(path)
383
+ if msg is None or msg.sender != sender:
384
+ continue
385
+ if msg.kind in ESCALATE_KINDS:
386
+ msg.dead_ts = _now_ms()
387
+ msg.reason = "sender_exited"
388
+ target = dead_dir(msg.to or "unknown") / path.name
389
+ try:
390
+ target.parent.mkdir(parents=True, exist_ok=True)
391
+ _write_message(target, msg)
392
+ path.unlink(missing_ok=True)
393
+ _log_event(
394
+ "dead_letter", id=msg.id, to=msg.to, kind=msg.kind,
395
+ attempts=msg.attempts, reason="sender_exited",
396
+ )
397
+ dead += 1
398
+ _escalate_dead_letter(msg, "sender_exited")
399
+ except OSError:
400
+ pass
401
+ else:
402
+ try:
403
+ path.unlink()
404
+ dropped += 1
405
+ except OSError:
406
+ pass
407
+ finally:
408
+ _release_lock(lock)
409
+
410
+ if dead or dropped:
411
+ _log_event("gc_sender", sender=sender, dead=dead, dropped=dropped)
412
+ return {"dead": dead, "dropped": dropped}
413
+
414
+
296
415
  # =============================================================================
297
416
  # Enqueue + broadcast
298
417
  # =============================================================================
@@ -494,12 +613,52 @@ def _bump_attempts(messages: list[Message], reason: str = "") -> int:
494
613
  return dead
495
614
 
496
615
 
497
- def flush_session(session: str) -> dict:
616
+ def _dedup_landed(session: str, messages: list[Message]) -> list[Message]:
617
+ """Consume (unlink) every message whose render() is already on scrollback.
618
+
619
+ The load-bearing #621 fix. ``safe_deliver`` confirms submission by polling
620
+ the input box back to empty; under host load that confirm false-negatives
621
+ even though the paste *landed* and the recipient saw it. Retaining a landed
622
+ message re-injects it on every idle tick — forever. So before (and after) a
623
+ paste we check the recipient's scrollback per-message: any message whose own
624
+ first-line fragment is visible has demonstrably landed → unlink it. Only
625
+ truly-absent messages stay pending.
626
+
627
+ Per-message keying (not the coalesced blob) so a partial landing consumes
628
+ exactly the visible subset. A strict fragment check (never the generic
629
+ ``"[Pasted text"`` placeholder fallback) so a stray placeholder can't mark
630
+ every message delivered. A message that scrolled past the 200-line window
631
+ reads as not-visible → kept (safe direction: retry, never silent-drop).
632
+ """
633
+ from .session_ready import message_on_scrollback, scrollback
634
+
635
+ capture = scrollback(session, 0)
636
+ consumed: list[Message] = []
637
+ for msg in messages:
638
+ if msg.path is None:
639
+ continue
640
+ if message_on_scrollback(capture, msg.render()):
641
+ msg.path.unlink(missing_ok=True)
642
+ consumed.append(msg)
643
+ if consumed:
644
+ _log_event(
645
+ "delivered_dedup", to=session, count=len(consumed),
646
+ kinds=[m.kind for m in consumed],
647
+ )
648
+ return consumed
649
+
650
+
651
+ def flush_session(session: str, force: bool = False) -> dict:
498
652
  """Attempt to drain one session's inbox now.
499
653
 
500
654
  Delivers oldest-first, coalescing all queued messages into a single paste
501
655
  (one submit) when the box is empty. On any refusal the messages stay put,
502
656
  their ``attempts`` bump, and over the cap they dead-letter. Never raises.
657
+
658
+ *force* (the ``msg flush --force`` escape hatch) bypasses the empty-box /
659
+ busy gate and pastes regardless — for an operator un-wedging a stuck queue,
660
+ accepting that it may land mid-draft. The ``safe_deliver`` safety guards
661
+ (gone / parked / non-agent / live-dialog) are never bypassed.
503
662
  """
504
663
  from . import prompt_router
505
664
 
@@ -511,42 +670,74 @@ def flush_session(session: str) -> dict:
511
670
  if not messages:
512
671
  return {"session": session, "delivered": 0, "deferred": False, "reason": "empty"}
513
672
 
514
- # Collision guard FIRST (cheap, and refuses dialogs/busy too via None).
515
- visible = prompt_router.capture(session, 0)
516
- box_content = prompt_router.input_box_content(visible)
517
-
518
- if box_content is None:
519
- # Target is busy (input box not located). Defer.
520
- dead = _bump_attempts(messages, "target_busy")
521
- _log_event("deferred", to=session, count=len(messages), reason="target_busy")
522
- return {
523
- "session": session, "delivered": 0, "deferred": True,
524
- "reason": "target_busy", "dead": dead,
525
- }
526
-
527
- if box_content != "":
528
- # Box is not empty. We never bypass this to protect human drafts. But
529
- # the "queued messages" placeholder is a BUSY signal, not a draft:
530
- # defer WITHOUT penalty (like target_busy) so a generating-with-queued
531
- # session doesn't burn report-backs toward dead-letter. Either way we
532
- # never paste — only the penalty decision differs.
533
- placeholder = prompt_router.is_queued_placeholder(box_content)
534
- reason = "queued_placeholder" if placeholder else "box_not_empty"
535
- dead = _bump_attempts(messages, reason)
536
- _log_event("deferred", to=session, count=len(messages), reason=reason)
537
- return {
538
- "session": session, "delivered": 0, "deferred": True,
539
- "reason": reason, "dead": dead,
540
- }
673
+ pre_consumed = 0
674
+ if not force:
675
+ # Collision guard FIRST (cheap, and refuses dialogs/busy too via None).
676
+ # But first: a prior tick may have LANDED these and false-negatived the
677
+ # confirm (#621). If they're already on scrollback, consume them now
678
+ # instead of waiting for the box to free up to re-paste a duplicate.
679
+ consumed = _dedup_landed(session, messages)
680
+ if consumed:
681
+ pre_consumed = len(consumed)
682
+ messages = [m for m in messages if m.path is not None and m.path.exists()]
683
+ if not messages:
684
+ return {
685
+ "session": session, "delivered": pre_consumed,
686
+ "deferred": False, "reason": "delivered",
687
+ }
688
+
689
+ visible = prompt_router.capture(session, 0)
690
+ box_content = prompt_router.input_box_content(visible)
691
+
692
+ if box_content is None:
693
+ # Target is busy (input box not located). Defer.
694
+ dead = _bump_attempts(messages, "target_busy")
695
+ _log_event("deferred", to=session, count=len(messages), reason="target_busy")
696
+ return {
697
+ "session": session, "delivered": pre_consumed, "deferred": True,
698
+ "reason": "target_busy", "dead": dead,
699
+ }
700
+
701
+ if box_content != "":
702
+ # Box is not empty. We never bypass this to protect human drafts. But
703
+ # the "queued messages" placeholder is a BUSY signal, not a draft:
704
+ # defer WITHOUT penalty (like target_busy) so a generating-with-queued
705
+ # session doesn't burn report-backs toward dead-letter. Either way we
706
+ # never paste — only the penalty decision differs.
707
+ placeholder = prompt_router.is_queued_placeholder(box_content)
708
+ reason = "queued_placeholder" if placeholder else "box_not_empty"
709
+ dead = _bump_attempts(messages, reason)
710
+ _log_event("deferred", to=session, count=len(messages), reason=reason)
711
+ return {
712
+ "session": session, "delivered": pre_consumed, "deferred": True,
713
+ "reason": reason, "dead": dead,
714
+ }
541
715
 
542
716
  rendered = "\n".join(m.render() for m in messages)
543
717
  delivered, reason = prompt_router.safe_deliver(session, 0, rendered)
544
718
  if not delivered:
545
- dead = _bump_attempts(messages, reason)
546
- _log_event("deferred", to=session, count=len(messages), reason=reason)
719
+ # delivery_unverified means the box-cleared confirm failed — but the
720
+ # paste may have LANDED. Consume any message now visible on scrollback
721
+ # (idempotent delivery) so a false-negative can't cause re-injection.
722
+ # Other reasons (gone/parked/non-agent/dialog) never pasted, so there's
723
+ # nothing to dedup.
724
+ consumed = (
725
+ _dedup_landed(session, messages)
726
+ if reason == "delivery_unverified"
727
+ else []
728
+ )
729
+ consumed_ids = {m.id for m in consumed}
730
+ remaining = [m for m in messages if m.id not in consumed_ids]
731
+ if not remaining:
732
+ return {
733
+ "session": session, "delivered": pre_consumed + len(consumed),
734
+ "deferred": False, "reason": "delivered",
735
+ }
736
+ dead = _bump_attempts(remaining, reason)
737
+ _log_event("deferred", to=session, count=len(remaining), reason=reason)
547
738
  return {
548
- "session": session, "delivered": 0, "deferred": True,
549
- "reason": reason, "dead": dead,
739
+ "session": session, "delivered": pre_consumed + len(consumed),
740
+ "deferred": True, "reason": reason, "dead": dead,
550
741
  }
551
742
 
552
743
  for msg in messages:
@@ -557,7 +748,7 @@ def flush_session(session: str) -> dict:
557
748
  kinds=[m.kind for m in messages],
558
749
  )
559
750
  return {
560
- "session": session, "delivered": len(messages),
751
+ "session": session, "delivered": pre_consumed + len(messages),
561
752
  "deferred": False, "reason": "delivered",
562
753
  }
563
754
  except Exception as exc: # draining must never break the watchdog
@@ -184,16 +184,44 @@ def msg_dead(session: str | None = None) -> str:
184
184
 
185
185
 
186
186
  @mcp.tool()
187
- def msg_flush(session: str | None = None) -> str:
188
- """Attempt a polite-message drain now (still gated on an empty box + safe target).
187
+ def msg_purge(session: str | None = None) -> str:
188
+ """Drop a session's PENDING polite messages the self-heal escape hatch.
189
+
190
+ Use this to un-wedge a recipient stuck re-seeing the same messages (or to
191
+ clear an inbox you know is stale). It drops the active drain queue outright —
192
+ no empty-box gate, no delivery. Dead-lettered (`msg_dead`) and passive
193
+ `ingest` messages are untouched; this is strictly the pending queue.
194
+
195
+ Args:
196
+ session: Session whose pending queue to drop (default: the caller).
197
+
198
+ Returns:
199
+ How many pending messages were dropped.
200
+ """
201
+ caller = session or get_caller_session()
202
+ args = ["msg", "purge"]
203
+ if caller:
204
+ args.append(caller)
205
+ data = run_agentwire_cmd(args)
206
+ if not data.get("success"):
207
+ return f"Failed to purge: {data.get('error', 'Unknown error')}"
208
+ return f"Purged {data.get('purged', 0)} pending message(s) from {data.get('session')}."
209
+
210
+
211
+ @mcp.tool()
212
+ def msg_flush(session: str | None = None, force: bool = False) -> str:
213
+ """Attempt a polite-message drain now (gated on an empty box + safe target).
189
214
 
190
215
  Messages drain automatically every ≤60s via the watchdog; use this to force a
191
- pass without waiting. It does NOT bypass the safety gates — a busy/parked/
192
- non-agent recipient is still deferred. Passive `ingest` messages are never
193
- drained (pull them with msg_pull).
216
+ pass without waiting. By default it does NOT bypass the safety gates — a
217
+ busy/parked/non-agent recipient is still deferred. Passive `ingest` messages
218
+ are never drained (pull them with msg_pull).
194
219
 
195
220
  Args:
196
221
  session: Session to flush (default: all sessions with queued messages).
222
+ force: Bypass the empty-box gate and paste anyway (requires `session`;
223
+ may land mid-draft). For un-wedging a stuck queue. Safety guards
224
+ (gone/parked/non-agent/live-dialog) are never bypassed.
197
225
 
198
226
  Returns:
199
227
  What was delivered or deferred.
@@ -201,6 +229,8 @@ def msg_flush(session: str | None = None) -> str:
201
229
  args = ["msg", "flush"]
202
230
  if session:
203
231
  args += ["-s", session]
232
+ if force:
233
+ args.append("--force")
204
234
  data = run_agentwire_cmd(args)
205
235
  if not data.get("success"):
206
236
  return f"Failed to flush: {data.get('error', 'Unknown error')}"
@@ -244,11 +244,35 @@ def cmd_msg_dead(args) -> int:
244
244
  return 0
245
245
 
246
246
 
247
+ def cmd_msg_purge(args) -> int:
248
+ """Drop a session's pending (undelivered) messages — the self-heal escape hatch."""
249
+ session = getattr(args, "session", None) or _current_session()
250
+ if not session:
251
+ print("No session (use the session name, or run inside one)")
252
+ if getattr(args, "json", False):
253
+ print(json.dumps({"success": False, "error": "no session"}))
254
+ return 1
255
+ removed = inbox.purge_pending(session)
256
+ if getattr(args, "json", False):
257
+ print(json.dumps({"success": True, "session": session, "purged": removed}))
258
+ return 0
259
+ print(f"Purged {removed} pending message(s) from {session}")
260
+ return 0
261
+
262
+
247
263
  def cmd_msg_flush(args) -> int:
248
- """Attempt a drain now (still gated on an empty box + safe target)."""
264
+ """Attempt a drain now (gated on an empty box + safe target unless --force)."""
249
265
  session = getattr(args, "session", None)
266
+ force = getattr(args, "force", False)
267
+ if force and not session:
268
+ msg = "--force requires -s <session> (refuses to force-drain every inbox)"
269
+ if getattr(args, "json", False):
270
+ print(json.dumps({"success": False, "error": msg}))
271
+ else:
272
+ print(msg)
273
+ return 1
250
274
  if session:
251
- result = inbox.flush_session(session)
275
+ result = inbox.flush_session(session, force=force)
252
276
  payload = {"success": True, **result}
253
277
  else:
254
278
  result = inbox.tick()
@@ -340,10 +364,25 @@ def register_msg_parser(subparsers) -> None:
340
364
  dead_parser.add_argument("--json", action="store_true", help="Output JSON")
341
365
  dead_parser.set_defaults(func=cmd_msg_dead)
342
366
 
343
- flush_parser = msg_sub.add_parser("flush", help="Attempt a drain now (still gated)")
367
+ purge_parser = msg_sub.add_parser(
368
+ "purge",
369
+ help="Drop a session's pending messages (self-heal a wedged inbox)",
370
+ )
371
+ purge_parser.add_argument(
372
+ "session", nargs="?", default=None,
373
+ help="Session to purge (default: current)",
374
+ )
375
+ purge_parser.add_argument("--json", action="store_true", help="Output JSON")
376
+ purge_parser.set_defaults(func=cmd_msg_purge)
377
+
378
+ flush_parser = msg_sub.add_parser("flush", help="Attempt a drain now (gated unless --force)")
344
379
  flush_parser.add_argument(
345
380
  "-s", "--session", default=None, help="Session to flush (default: all)"
346
381
  )
382
+ flush_parser.add_argument(
383
+ "--force", action="store_true",
384
+ help="Bypass the empty-box gate and paste anyway (requires -s; may land mid-draft)",
385
+ )
347
386
  flush_parser.add_argument("--json", action="store_true", help="Output JSON")
348
387
  flush_parser.set_defaults(func=cmd_msg_flush)
349
388
 
@@ -20,7 +20,6 @@ from .core import (
20
20
  _portal_auth_headers,
21
21
  _post_desktop_notification,
22
22
  )
23
- from .project_config import get_parent_from_config
24
23
 
25
24
 
26
25
  def cmd_notify_parent(args) -> int:
@@ -51,11 +50,30 @@ def cmd_notify_parent(args) -> int:
51
50
  current_session = pane_manager.get_current_session()
52
51
  current_pane = pane_manager.get_current_pane_index()
53
52
 
54
- # If no explicit target, try parent from config
55
- if not target_session:
56
- parent = get_parent_from_config()
57
- if parent:
58
- target_session = parent
53
+ # --on-idle: the idle hook is reporting that a pane-0 session went idle.
54
+ # Infrastructure services (portal, tts/stt, scheduler, the idle-nag bridge,
55
+ # custom services) cycle active→idle constantly and are NOT delegated work,
56
+ # so they must never fire a "child finished" ping at their parent. A
57
+ # worktree / `agentwire new` child is not a service and is unaffected.
58
+ if getattr(args, 'on_idle', False) and current_session:
59
+ from agentwire import services
60
+
61
+ if services.is_service_session(current_session):
62
+ return _output_result(True, json_mode, "", skipped="service-session")
63
+
64
+ # If no explicit target, resolve the parent through the SAME precedence the
65
+ # prompt router uses (worker pane → pane 0; else creator recorded at
66
+ # `agentwire new` time; else `.agentwire.yml parent:`). The old path looked
67
+ # only at `.agentwire.yml`, so a worktree/`agentwire new` child — whose
68
+ # parent lives in session metadata, not config — resolved to nothing and its
69
+ # idle notification silently dropped (the parent never heard the child go
70
+ # idle). resolve_parent reads the creator metadata, closing that gap.
71
+ if not target_session and current_session is not None and current_pane is not None:
72
+ from agentwire import prompt_router
73
+
74
+ resolved = prompt_router.resolve_parent(current_session, current_pane)
75
+ if resolved:
76
+ target_session = resolved[0]
59
77
 
60
78
  # Build notification message (--raw sends verbatim — queued messages
61
79
  # already carry their own [WORKER SUMMARY ...] / [PROMPT ...] headers)
@@ -255,6 +273,9 @@ def register_notify_parser(subparsers) -> None:
255
273
  notify_cmd_parser.add_argument("-q", "--quiet", action="store_true", help="Suppress output")
256
274
  notify_cmd_parser.add_argument("--raw", action="store_true",
257
275
  help="Send the message verbatim (no [NOTIFY from ...] prefix)")
276
+ notify_cmd_parser.add_argument("--on-idle", dest="on_idle", action="store_true",
277
+ help="Idle-hook mode: suppress the notify if the current session "
278
+ "is an infrastructure service (they cycle idle constantly)")
258
279
  notify_cmd_parser.add_argument("--json", action="store_true", help="Output as JSON")
259
280
  notify_cmd_parser.set_defaults(func=cmd_notify_parent)
260
281
 
@@ -470,6 +470,7 @@ services:
470
470
  print(f"{BOLD}Next steps:{RESET}")
471
471
  print(f" 1. {CYAN}agentwire portal start{RESET}")
472
472
  print(f" 2. Open {CYAN}{portal_open_url}{RESET} in Chrome — voice works immediately")
473
+ print(f" 3. {CYAN}agentwire dev{RESET} — a helper session that walks you through setup, wires up your projects, and explains the system")
473
474
  print()
474
475
  print_info("Run 'agentwire init --assisted' to configure TTS/STT with Claude's help.")
475
476
  return 0
@@ -722,6 +722,15 @@ def cmd_kill(args) -> int:
722
722
  except OSError:
723
723
  pass
724
724
 
725
+ # GC this sender's still-pending outbound across every recipient inbox (#621)
726
+ # so report-backs it left undelivered don't accumulate. Load-bearing kinds
727
+ # dead-letter (and escalate via owner email); the rest are dropped.
728
+ try:
729
+ from . import inbox
730
+ inbox.gc_sender(session)
731
+ except Exception:
732
+ pass
733
+
725
734
  _notify_portal_sessions_changed()
726
735
 
727
736
  if json_mode:
@@ -193,13 +193,19 @@ class ProjectConfig:
193
193
 
194
194
 
195
195
  def find_project_config(start_path: Optional[Path] = None) -> Optional[Path]:
196
- """Find .agentwire.yml by walking up from start_path.
196
+ """Find project config by walking up from start_path.
197
+
198
+ At each directory level, a local untracked ``.agentwire.yml`` wins over a
199
+ committed ``.agentwire.yml.example`` template. The ``.example`` fallback lets
200
+ a repo (e.g. agentwire-dev itself) ship a sensible default config that a
201
+ fresh clone uses out of the box, while the owner's personal, gitignored
202
+ ``.agentwire.yml`` overrides it when present. See #620.
197
203
 
198
204
  Args:
199
205
  start_path: Directory to start searching from. Defaults to cwd.
200
206
 
201
207
  Returns:
202
- Path to .agentwire.yml if found, None otherwise.
208
+ Path to the resolved config file if found, None otherwise.
203
209
  """
204
210
  if start_path is None:
205
211
  start_path = Path.cwd()
@@ -207,17 +213,17 @@ def find_project_config(start_path: Optional[Path] = None) -> Optional[Path]:
207
213
  start_path = Path(start_path).resolve()
208
214
 
209
215
  current = start_path
210
- while current != current.parent:
211
- config_file = current / ".agentwire.yml"
212
- if config_file.exists():
213
- return config_file
216
+ while True:
217
+ live = current / ".agentwire.yml"
218
+ if live.exists():
219
+ return live
220
+ example = current / ".agentwire.yml.example"
221
+ if example.exists():
222
+ return example
223
+ if current == current.parent:
224
+ break
214
225
  current = current.parent
215
226
 
216
- # Check root
217
- config_file = current / ".agentwire.yml"
218
- if config_file.exists():
219
- return config_file
220
-
221
227
  return None
222
228
 
223
229
 
@@ -234,7 +240,10 @@ def load_project_config(path: Optional[Path] = None) -> Optional[ProjectConfig]:
234
240
  if path is None:
235
241
  config_path = find_project_config()
236
242
  elif path.is_dir():
237
- config_path = path / ".agentwire.yml"
243
+ # Local .agentwire.yml wins over a committed .agentwire.yml.example (#620).
244
+ live = path / ".agentwire.yml"
245
+ example = path / ".agentwire.yml.example"
246
+ config_path = live if live.exists() else example
238
247
  else:
239
248
  config_path = path
240
249