agentwire-dev 1.50.1__tar.gz → 1.52.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.52.0/.agentwire.yml.example +14 -0
  2. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/.gitignore +3 -0
  3. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/PKG-INFO +5 -1
  4. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/README.md +4 -0
  5. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/__init__.py +1 -1
  6. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/agents/tmux.py +3 -3
  7. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/config.py +7 -0
  8. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/inbox.py +249 -37
  9. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/listen.py +67 -26
  10. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/mcp_msg.py +35 -5
  11. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/msg_cli.py +42 -3
  12. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/notify_cli.py +0 -1
  13. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/onboarding.py +1 -0
  14. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/pane_cli.py +9 -0
  15. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/project_config.py +21 -12
  16. agentwire_dev-1.52.0/agentwire/roles/contributor.md +83 -0
  17. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/routes/voice.py +14 -1
  18. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/security.py +165 -4
  19. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/send_cli.py +3 -3
  20. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/server.py +36 -19
  21. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/session_cli.py +28 -13
  22. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/session_ready.py +59 -2
  23. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/artifact-window.js +21 -24
  24. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/stt/__init__.py +19 -0
  25. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/system_cli.py +19 -4
  26. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/tts/engines/kokoro.py +29 -6
  27. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/voiceclone.py +64 -18
  28. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/communication/hammerspoon.md +1 -1
  29. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/quickstart.md +10 -0
  30. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/sessions/messaging.md +43 -3
  31. agentwire_dev-1.52.0/tests/integration/test_send_verified_tmux.py +186 -0
  32. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_inbox.py +255 -1
  33. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_portal_api.py +138 -0
  34. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_project_config.py +30 -0
  35. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_roles.py +1 -1
  36. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_security.py +107 -0
  37. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_session_ready.py +38 -0
  38. agentwire_dev-1.52.0/tests/unit/test_shared_dir_guard.py +44 -0
  39. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_stt_backend.py +21 -1
  40. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_tts_local.py +31 -3
  41. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/.claude/commands/handoff.md +0 -0
  42. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/.claude/skills/wiki/SKILL.md +0 -0
  43. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/.github/FUNDING.yml +0 -0
  44. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  45. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  46. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/.github/ISSUE_TEMPLATE/question.md +0 -0
  47. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  48. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/.github/workflows/pytest.yml +0 -0
  49. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/.github/workflows/ruff.yml +0 -0
  50. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/.github/workflows/security.yml +0 -0
  51. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/.github/workflows/tts-smoke.yml +0 -0
  52. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/AGENTS.md +0 -0
  53. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/CODE_OF_CONDUCT.md +0 -0
  54. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/CONTRIBUTING.md +0 -0
  55. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/Dockerfile.local +0 -0
  56. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/LICENSE +0 -0
  57. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/RELEASING.md +0 -0
  58. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/SECURITY.md +0 -0
  59. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/SPONSORS.md +0 -0
  60. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/__main__.py +0 -0
  61. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/agents/__init__.py +0 -0
  62. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/agents/base.py +0 -0
  63. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/cached_status.py +0 -0
  64. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/channels/__init__.py +0 -0
  65. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/channels/base.py +0 -0
  66. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/channels/email.py +0 -0
  67. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/channels/push.py +0 -0
  68. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/channels/quo.py +0 -0
  69. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/channels_cli.py +0 -0
  70. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/completion.py +0 -0
  71. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/core.py +0 -0
  72. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/council/__init__.py +0 -0
  73. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/council/cli.py +0 -0
  74. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/council/inbox.py +0 -0
  75. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/council/state.py +0 -0
  76. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/council/view.py +0 -0
  77. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/devices.py +0 -0
  78. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/diff_cli.py +0 -0
  79. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/doctor_cli.py +0 -0
  80. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/doctor_voice.py +0 -0
  81. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/ensure_cli.py +0 -0
  82. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/fetch.py +0 -0
  83. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/handoff/__init__.py +0 -0
  84. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/handoff/git_state.py +0 -0
  85. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/handoff/instructions.py +0 -0
  86. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/handoff/parser.py +0 -0
  87. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/handoff/renderer.py +0 -0
  88. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/handoff/schema.py +0 -0
  89. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/handoff_cli.py +0 -0
  90. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/history.py +0 -0
  91. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/history_cli.py +0 -0
  92. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/hooks/__init__.py +0 -0
  93. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/hooks/agentwire-permission.sh +0 -0
  94. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/hooks/damage-control/__init__.py +0 -0
  95. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/hooks/damage-control/audit_logger.py +0 -0
  96. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/hooks/damage-control/bash-tool-damage-control.py +0 -0
  97. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/hooks/damage-control/edit-tool-damage-control.py +0 -0
  98. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/hooks/damage-control/mcp-tool-damage-control.py +0 -0
  99. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/hooks/damage-control/read-tool-damage-control.py +0 -0
  100. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/hooks/damage-control/rules/agentwire.yaml +0 -0
  101. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/hooks/damage-control/rules/aws.yaml +0 -0
  102. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/hooks/damage-control/rules/cloud-hosting.yaml +0 -0
  103. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/hooks/damage-control/rules/containers.yaml +0 -0
  104. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/hooks/damage-control/rules/control-plane.yaml +0 -0
  105. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/hooks/damage-control/rules/core.yaml +0 -0
  106. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/hooks/damage-control/rules/databases.yaml +0 -0
  107. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/hooks/damage-control/rules/firebase.yaml +0 -0
  108. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/hooks/damage-control/rules/gcp.yaml +0 -0
  109. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/hooks/damage-control/rules/git.yaml +0 -0
  110. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/hooks/damage-control/rules/gws.yaml +0 -0
  111. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/hooks/damage-control/rules/infrastructure.yaml +0 -0
  112. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/hooks/damage-control/rules/outbound.yaml +0 -0
  113. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/hooks/damage-control/rules/publish.yaml +0 -0
  114. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/hooks/damage-control/rules/remote.yaml +0 -0
  115. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/hooks/damage-control/write-tool-damage-control.py +0 -0
  116. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/hooks/idle-handler.sh +0 -0
  117. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/hooks/queue-processor.sh +0 -0
  118. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/hooks_cli.py +0 -0
  119. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/limits_cli.py +0 -0
  120. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/locking.py +0 -0
  121. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/machine_cli.py +0 -0
  122. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/mcp_channels.py +0 -0
  123. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/mcp_cli.py +0 -0
  124. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/mcp_core.py +0 -0
  125. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/mcp_council.py +0 -0
  126. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/mcp_desktop.py +0 -0
  127. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/mcp_handoff.py +0 -0
  128. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/mcp_history.py +0 -0
  129. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/mcp_listen.py +0 -0
  130. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/mcp_lock.py +0 -0
  131. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/mcp_machine.py +0 -0
  132. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/mcp_notify.py +0 -0
  133. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/mcp_pane.py +0 -0
  134. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/mcp_projects.py +0 -0
  135. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/mcp_scheduler.py +0 -0
  136. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/mcp_scratchpad.py +0 -0
  137. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/mcp_server.py +0 -0
  138. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/mcp_services.py +0 -0
  139. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/mcp_session.py +0 -0
  140. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/mcp_status.py +0 -0
  141. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/mcp_task.py +0 -0
  142. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/mcp_tunnels.py +0 -0
  143. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/mcp_voice.py +0 -0
  144. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/mcp_voiceclone.py +0 -0
  145. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/mcp_wiki.py +0 -0
  146. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/mcp_worktree.py +0 -0
  147. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/network.py +0 -0
  148. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/pane_manager.py +0 -0
  149. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/portal_cli.py +0 -0
  150. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/projects.py +0 -0
  151. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/prompt_router.py +0 -0
  152. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/prompts_cli.py +0 -0
  153. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/push_cli.py +0 -0
  154. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/push_store.py +0 -0
  155. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/repo_cli.py +0 -0
  156. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/research.py +0 -0
  157. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/roles/__init__.py +0 -0
  158. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/roles/agentwire.md +0 -0
  159. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/roles/anchor.md +0 -0
  160. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/roles/chatbot.md +0 -0
  161. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/roles/correspondent.md +0 -0
  162. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/roles/council-brain.md +0 -0
  163. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/roles/council-conscience.md +0 -0
  164. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/roles/council-critic.md +0 -0
  165. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/roles/council-devils-advocate.md +0 -0
  166. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/roles/council-gut.md +0 -0
  167. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/roles/council-historian.md +0 -0
  168. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/roles/council-member.md +0 -0
  169. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/roles/council-orchestrator.md +0 -0
  170. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/roles/init.md +0 -0
  171. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/roles/notifications.md +0 -0
  172. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/roles/orchestrator.md +0 -0
  173. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/roles/soul.md +0 -0
  174. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/roles/task-runner.md +0 -0
  175. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/roles/voice.md +0 -0
  176. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/roles/worker.md +0 -0
  177. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/roles/worktree-session.md +0 -0
  178. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/roles_cli.py +0 -0
  179. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/routes/__init__.py +0 -0
  180. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/routes/artifacts.py +0 -0
  181. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/routes/config.py +0 -0
  182. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/routes/council.py +0 -0
  183. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/routes/desktop.py +0 -0
  184. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/routes/history.py +0 -0
  185. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/routes/machines.py +0 -0
  186. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/routes/notify.py +0 -0
  187. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/routes/permission.py +0 -0
  188. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/routes/projects.py +0 -0
  189. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/routes/push.py +0 -0
  190. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/routes/safety.py +0 -0
  191. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/routes/scheduler.py +0 -0
  192. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/routes/scratchpad.py +0 -0
  193. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/routes/sessions.py +0 -0
  194. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/routes/sessions_admin.py +0 -0
  195. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/safety/__init__.py +0 -0
  196. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/safety/_core.py +0 -0
  197. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/safety_cli.py +0 -0
  198. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/safety_commands.py +0 -0
  199. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/scheduler.py +0 -0
  200. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/scheduler_cli.py +0 -0
  201. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/scratchpad.py +0 -0
  202. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/services.py +0 -0
  203. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/session_context.py +0 -0
  204. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/ssh.py +0 -0
  205. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/agentwire-splash-logo-layers--agentwire-text.png +0 -0
  206. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/agentwire-splash-logo-layers--echo-claw-fg.png +0 -0
  207. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/agentwire-splash-logo-layers--echo.png +0 -0
  208. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/agentwire-splash-logo-layers--telephone.png +0 -0
  209. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/agentwire-splash-logo-layers--tree.png +0 -0
  210. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/announcements.json +0 -0
  211. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/css/desktop.css +0 -0
  212. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/css/mobile.css +0 -0
  213. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/favicon.png +0 -0
  214. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/icons/sessions/bear.webp +0 -0
  215. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/icons/sessions/cat.webp +0 -0
  216. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/icons/sessions/crown.webp +0 -0
  217. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/icons/sessions/custom/agentwire-portal.webp +0 -0
  218. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/icons/sessions/custom/agentwire-tts.webp +0 -0
  219. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/icons/sessions/custom/agentwire.webp +0 -0
  220. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/icons/sessions/deer.webp +0 -0
  221. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/icons/sessions/drone.webp +0 -0
  222. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/icons/sessions/eagle.webp +0 -0
  223. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/icons/sessions/fox.webp +0 -0
  224. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/icons/sessions/hawk.webp +0 -0
  225. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/icons/sessions/horse.webp +0 -0
  226. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/icons/sessions/lion.webp +0 -0
  227. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/icons/sessions/rabbit.webp +0 -0
  228. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/icons/sessions/robot.webp +0 -0
  229. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/icons/sessions/tiger.webp +0 -0
  230. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/icons/sessions/wolf.webp +0 -0
  231. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/img/icon-192.png +0 -0
  232. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/img/icon-512.png +0 -0
  233. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/.gitkeep +0 -0
  234. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/announcement-modal.js +0 -0
  235. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/api.js +0 -0
  236. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/collage.js +0 -0
  237. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/command-palette.js +0 -0
  238. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/council-window.js +0 -0
  239. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/dead-key-suppressor.js +0 -0
  240. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/desktop-manager.js +0 -0
  241. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/desktop.js +0 -0
  242. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/help-modal.js +0 -0
  243. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/icon-manager.js +0 -0
  244. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/mobile.js +0 -0
  245. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/notification-prefs.js +0 -0
  246. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/notifications-panel.js +0 -0
  247. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/push-client.js +0 -0
  248. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/review-window.js +0 -0
  249. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/safety-shared.js +0 -0
  250. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/safety-window.js +0 -0
  251. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/scratchpad.js +0 -0
  252. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/service-classification.js +0 -0
  253. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/session-id.js +0 -0
  254. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/session-window.js +0 -0
  255. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/shortcuts.js +0 -0
  256. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/sidebar/artifacts-section.js +0 -0
  257. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/sidebar/config-section.js +0 -0
  258. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/sidebar/council-section.js +0 -0
  259. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/sidebar/machines-section.js +0 -0
  260. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/sidebar/projects-section.js +0 -0
  261. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/sidebar/safety-section.js +0 -0
  262. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/sidebar/scheduler-section.js +0 -0
  263. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/sidebar/services-section.js +0 -0
  264. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/sidebar/sessions-section.js +0 -0
  265. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/sidebar.js +0 -0
  266. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/terminal-font-prefs.js +0 -0
  267. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/tile-manager.js +0 -0
  268. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/toast.js +0 -0
  269. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/token-modal.js +0 -0
  270. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/utils/ansi.js +0 -0
  271. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/utils/swipe.js +0 -0
  272. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/voice/autosend-prefs.js +0 -0
  273. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/voice/browser-stt.js +0 -0
  274. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/voice/browser-tts.js +0 -0
  275. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/voice/jargon.js +0 -0
  276. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/voice/prompt.js +0 -0
  277. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/js/winbox.bundle.min.js +0 -0
  278. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/manifest.webmanifest +0 -0
  279. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/static/service-worker.js +0 -0
  280. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/stt/base.py +0 -0
  281. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/stt/cloud.py +0 -0
  282. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/stt/engine.py +0 -0
  283. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/stt/local.py +0 -0
  284. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/stt/server_backend.py +0 -0
  285. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/stt/stt_server.py +0 -0
  286. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/tasks.py +0 -0
  287. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/templates/__init__.py +0 -0
  288. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/templates/base.html +0 -0
  289. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/templates/desktop.html +0 -0
  290. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/templates/email_notification.html +0 -0
  291. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/templates/handoff/show-the-story.html.j2 +0 -0
  292. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/templates/handoff/theme.css.j2 +0 -0
  293. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/templates/mobile.html +0 -0
  294. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/templates/pair.html +0 -0
  295. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/templates/tmux.conf +0 -0
  296. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/templating.py +0 -0
  297. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/tooldefs/aws.yaml +0 -0
  298. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/tooldefs/docker.yaml +0 -0
  299. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/tooldefs/gcp.yaml +0 -0
  300. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/tooldefs/gh.yaml +0 -0
  301. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/tooldefs/git.yaml +0 -0
  302. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/tooldefs/gws.yaml +0 -0
  303. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/tooldefs/kubectl.yaml +0 -0
  304. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/tooldefs/npm.yaml +0 -0
  305. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/tooldefs/terraform.yaml +0 -0
  306. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/tooldefs/uv.yaml +0 -0
  307. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/tts/__init__.py +0 -0
  308. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/tts/audio.py +0 -0
  309. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/tts/base.py +0 -0
  310. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/tts/engines/__init__.py +0 -0
  311. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/tts/engines/chatterbox.py +0 -0
  312. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/tts/engines/zonos.py +0 -0
  313. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/tts/kokoro_server.py +0 -0
  314. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/tts/local.py +0 -0
  315. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/tts/registry.py +0 -0
  316. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/tts_cli.py +0 -0
  317. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/tts_server.py +0 -0
  318. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/tunnels.py +0 -0
  319. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/tunnels_cli.py +0 -0
  320. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/usage_limit.py +0 -0
  321. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/utils/__init__.py +0 -0
  322. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/utils/chunker.py +0 -0
  323. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/utils/event_log.py +0 -0
  324. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/utils/file_io.py +0 -0
  325. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/utils/paths.py +0 -0
  326. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/utils/speech.py +0 -0
  327. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/utils/subprocess.py +0 -0
  328. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/validation.py +0 -0
  329. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/voice_status.py +0 -0
  330. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/voices/darren.wav +0 -0
  331. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/voices/default.wav +0 -0
  332. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/voices/jessica.wav +0 -0
  333. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/voices/lisa.wav +0 -0
  334. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/voices/may.wav +0 -0
  335. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/wiki.py +0 -0
  336. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/wiki_audit.py +0 -0
  337. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/wiki_cli.py +0 -0
  338. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/worktree.py +0 -0
  339. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/agentwire/worktree_registry.py +0 -0
  340. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/decisions/obsidian-second-brain.md +0 -0
  341. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/logo.png +0 -0
  342. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/INDEX.md +0 -0
  343. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/architecture.md +0 -0
  344. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/briefing-mode.md +0 -0
  345. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/communication/channels.md +0 -0
  346. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/communication/handoff.md +0 -0
  347. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/concepts.md +0 -0
  348. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/council.md +0 -0
  349. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/deployment/remote-access.md +0 -0
  350. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/deployment/remote-machines.md +0 -0
  351. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/glossary.md +0 -0
  352. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/integrations/gws-google-workspace-cli.md +0 -0
  353. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/internals/damage-control.md +0 -0
  354. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/internals/parallel-refactor.md +0 -0
  355. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/internals/portal.md +0 -0
  356. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/internals/shell-escaping.md +0 -0
  357. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/internals/troubleshooting.md +0 -0
  358. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/internals/window-collage.md +0 -0
  359. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/research/briefing-mode-feasibility.md +0 -0
  360. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/research/orchestration-transport-alternatives.md +0 -0
  361. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/scheduling/scheduled-workloads.md +0 -0
  362. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/security/damage-control-hardening.md +0 -0
  363. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/security/pip-audit.md +0 -0
  364. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/security/remote-access-hardening.md +0 -0
  365. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/security/secrets.md +0 -0
  366. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/services.md +0 -0
  367. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/sessions/claude-code-auto-mode.md +0 -0
  368. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/sessions/pi.md +0 -0
  369. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/sessions/prompt-routing.md +0 -0
  370. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/sessions/window-sizing.md +0 -0
  371. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/sessions/worktree-sessions.md +0 -0
  372. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/usage-limit-recovery.md +0 -0
  373. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/voice/shim-contract.md +0 -0
  374. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/voice/stt-cloud.md +0 -0
  375. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/voice/stt-self-hosted.md +0 -0
  376. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/docs/wiki/voice/tts-self-hosted.md +0 -0
  377. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/pyproject.toml +0 -0
  378. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/conftest.py +0 -0
  379. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/e2e/test_portal_ui.py +0 -0
  380. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/fixtures/sample_agentwire.yml +0 -0
  381. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/fixtures/sample_config.yaml +0 -0
  382. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/fixtures/sample_scheduler.yaml +0 -0
  383. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/integration/test_council_portal.py +0 -0
  384. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/integration/test_scheduler_board.py +0 -0
  385. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/integration/test_server_websockets.py +0 -0
  386. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/integration/test_worktree_cmd.py +0 -0
  387. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_announcements.py +0 -0
  388. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_build_agent_command.py +0 -0
  389. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_channels.py +0 -0
  390. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_cli_commands.py +0 -0
  391. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_cli_output.py +0 -0
  392. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_cli_smoke.py +0 -0
  393. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_config.py +0 -0
  394. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_control_plane_protection.py +0 -0
  395. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_council_cli.py +0 -0
  396. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_council_inbox.py +0 -0
  397. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_council_state.py +0 -0
  398. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_council_view.py +0 -0
  399. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_damage_control_bypass.py +0 -0
  400. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_damage_control_hooks.py +0 -0
  401. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_damage_control_sync.py +0 -0
  402. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_devices.py +0 -0
  403. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_diff_cli.py +0 -0
  404. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_doctor_voice.py +0 -0
  405. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_event_log.py +0 -0
  406. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_file_io.py +0 -0
  407. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_handoff_git_state.py +0 -0
  408. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_handoff_instructions.py +0 -0
  409. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_handoff_parser.py +0 -0
  410. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_handoff_renderer.py +0 -0
  411. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_history.py +0 -0
  412. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_hooks_install.py +0 -0
  413. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_idle_handler.py +0 -0
  414. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_idle_nag.py +0 -0
  415. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_init_command.py +0 -0
  416. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_limits_tick_isolation.py +0 -0
  417. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_locking.py +0 -0
  418. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_mcp_server.py +0 -0
  419. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_mcp_tools_args.py +0 -0
  420. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_msg_cli.py +0 -0
  421. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_prompt_router.py +0 -0
  422. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_push.py +0 -0
  423. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_rebuild_guard.py +0 -0
  424. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_repo_cli.py +0 -0
  425. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_routes_snapshot.py +0 -0
  426. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_safety_commands.py +0 -0
  427. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_safety_disabled_rules.py +0 -0
  428. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_safety_escape_hatch.py +0 -0
  429. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_safety_heal.py +0 -0
  430. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_safety_kill_switch.py +0 -0
  431. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_scheduler.py +0 -0
  432. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_scheduler_parsing.py +0 -0
  433. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_scratchpad.py +0 -0
  434. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_server_async.py +0 -0
  435. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_server_pure.py +0 -0
  436. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_services.py +0 -0
  437. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_session_context.py +0 -0
  438. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_skill_install.py +0 -0
  439. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_speech_tags.py +0 -0
  440. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_ssh.py +0 -0
  441. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_stt_cloud.py +0 -0
  442. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_stt_engine.py +0 -0
  443. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_task_cli.py +0 -0
  444. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_tasks.py +0 -0
  445. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_templating.py +0 -0
  446. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_terminal_resize.py +0 -0
  447. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_tmux_template.py +0 -0
  448. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_tts_engine_resolution.py +0 -0
  449. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_tunnels_probe.py +0 -0
  450. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_usage_limit.py +0 -0
  451. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_validation.py +0 -0
  452. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_voice_status.py +0 -0
  453. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_wiki.py +0 -0
  454. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_wiki_audit.py +0 -0
  455. {agentwire_dev-1.50.1 → agentwire_dev-1.52.0}/tests/unit/test_worktree.py +0 -0
  456. {agentwire_dev-1.50.1 → agentwire_dev-1.52.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
@@ -60,3 +60,6 @@ snapshot_report.html
60
60
 
61
61
  # Node
62
62
  node_modules/
63
+
64
+ # Private security-audit findings (never publish — see SECURITY.md)
65
+ /security-audit/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentwire-dev
3
- Version: 1.50.1
3
+ Version: 1.52.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.1"
3
+ __version__ = "1.52.0"
@@ -236,11 +236,11 @@ class TmuxAgent(AgentBackend):
236
236
  # Use base64 + load-buffer on remote to avoid PTY flooding
237
237
  import base64
238
238
  encoded = base64.b64encode(text.encode()).decode()
239
+ # Pipe straight into the tmux buffer — no on-disk temp that could
240
+ # expose message content or be pre-planted on the remote host
239
241
  cmd = (
240
- f"echo {shlex.quote(encoded)} | base64 -d > /tmp/aw-send-$$.txt && "
241
- f"tmux load-buffer /tmp/aw-send-$$.txt && "
242
+ f"echo {shlex.quote(encoded)} | base64 -d | tmux load-buffer - && "
242
243
  f"tmux paste-buffer -t {shlex.quote(session_name)} && "
243
- f"rm -f /tmp/aw-send-$$.txt && "
244
244
  f"sleep 0.2 && "
245
245
  f"tmux send-keys -t {shlex.quote(session_name)} Enter"
246
246
  )
@@ -74,6 +74,12 @@ class ServerConfig:
74
74
  # bearer token then transits in cleartext. Required to start a non-loopback
75
75
  # bind without TLS — the BYO-tunnel case where the tunnel terminates TLS.
76
76
  allow_insecure: bool = False
77
+ # Emit HTTP Strict-Transport-Security. Leave OFF for self-signed /
78
+ # localhost / LAN — HSTS + a self-signed cert locks the browser out of the
79
+ # portal if the cert ever rotates. Enable ONLY when serving a real
80
+ # (CA-signed) cert directly. Behind Cloudflare Tunnel / a reverse proxy,
81
+ # set HSTS at the edge instead.
82
+ hsts: bool = False
77
83
 
78
84
 
79
85
  @dataclass
@@ -590,6 +596,7 @@ def _dict_to_config(data: dict) -> Config:
590
596
  # "" (explicit disable) must survive — don't collapse it to None.
591
597
  auth_token=server_data.get("auth_token"),
592
598
  allow_insecure=bool(server_data.get("allow_insecure", False)),
599
+ hsts=bool(server_data.get("hsts", False)),
593
600
  )
594
601
 
595
602
  # Projects
@@ -30,6 +30,7 @@ from __future__ import annotations
30
30
  import errno
31
31
  import json
32
32
  import os
33
+ import re
33
34
  import time
34
35
  import uuid
35
36
  from dataclasses import dataclass
@@ -114,9 +115,23 @@ class Message:
114
115
  "ref": self.ref,
115
116
  }
116
117
 
118
+ def short_id(self) -> str:
119
+ """The 6-char uuid tail of ``id`` (the ``{epoch_ns}-{uuid6}`` suffix)."""
120
+ return self.id.rsplit("-", 1)[-1]
121
+
117
122
  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}"
123
+ """The one-line message injected on delivery (mirrors [NOTIFY from …]).
124
+
125
+ The trailing ``⟨#id6⟩`` token makes every delivered line UNIQUE on the
126
+ recipient's screen (#621). Idempotent-redelivery dedup matches the full
127
+ rendered line on scrollback; without a unique tail a shorter message
128
+ whose text is a prefix of a longer same-sender/kind one (or two
129
+ identical-text report-backs) would substring-collide and be consumed
130
+ without delivery. The token is appended at the END so the prefix-based
131
+ landing checks (``derive_check_fragment`` / ``message_visible``) are
132
+ unchanged.
133
+ """
134
+ return f"[MSG from {self.sender} · {self.kind}] {self.text} ⟨#{self.short_id()}⟩"
120
135
 
121
136
 
122
137
  def _now_ms() -> int:
@@ -134,8 +149,28 @@ def _short_uuid() -> str:
134
149
  return uuid.uuid4().hex[:6]
135
150
 
136
151
 
152
+ # Session names are agent-controlled (msg_send `to`), so they must never be
153
+ # able to path-traverse out of INBOX_ROOT. Worktree session names legitimately
154
+ # contain `/` (they nest one directory level per segment — see module
155
+ # docstring), so segments are validated individually; `..`, absolute paths,
156
+ # and empty names/segments are rejected.
157
+ _SESSION_RE = re.compile(r"^[A-Za-z0-9._@-]+(?:/[A-Za-z0-9._@-]+)*$")
158
+
159
+
160
+ def _validate_session(session: str) -> str:
161
+ if not session or not _SESSION_RE.match(session) or ".." in session.split("/"):
162
+ raise ValueError(f"invalid session name: {session!r}")
163
+ return session
164
+
165
+
137
166
  def session_dir(session: str) -> Path:
138
- return INBOX_ROOT / session
167
+ _validate_session(session)
168
+ path = INBOX_ROOT / session
169
+ # Belt and braces: the regex already forbids traversal, but confine the
170
+ # result to INBOX_ROOT so a validator regression can't escape it.
171
+ if not path.resolve().is_relative_to(INBOX_ROOT.resolve()):
172
+ raise ValueError(f"invalid session name: {session!r}")
173
+ return path
139
174
 
140
175
 
141
176
  def dead_dir(session: str) -> Path:
@@ -293,6 +328,111 @@ def purge_dead(session: "str | None" = None, before_ms: "int | None" = None) ->
293
328
  return removed
294
329
 
295
330
 
331
+ def purge_pending(session: str) -> int:
332
+ """Drop a session's *pending* (undelivered) messages; return how many.
333
+
334
+ The self-heal escape hatch (#621): when a recipient is wedged into a
335
+ redelivery loop, the only prior recovery was hand-moving JSON files — which
336
+ the recipient's own Bash hook blocks (``rm``). This drops the pending queue
337
+ outright, no empty-box gate, no delivery. Passive (``ingest/``) and dead
338
+ (``dead/``) messages are untouched — this is strictly the active drain queue.
339
+ """
340
+ # Serialize against an in-flight flush_session via the per-session drain lock
341
+ # so we don't yank a file mid-delivery. If a flush holds the lock we still
342
+ # proceed (the operator wants the queue gone) — unlinking under it is benign
343
+ # because flush copies messages into memory first and its own unlink is
344
+ # missing_ok.
345
+ lock = _acquire_lock(session)
346
+ try:
347
+ paths = pending_files(session)
348
+ removed = 0
349
+ for path in paths:
350
+ try:
351
+ path.unlink()
352
+ removed += 1
353
+ except OSError:
354
+ pass
355
+ if removed:
356
+ _log_event("purged_pending", session=session, count=removed)
357
+ return removed
358
+ finally:
359
+ _release_lock(lock)
360
+
361
+
362
+ def gc_sender(sender: str) -> dict:
363
+ """Garbage-collect an exited sender's still-pending outbound (#621).
364
+
365
+ Messages live keyed by *recipient*, so when a worktree/session exits nothing
366
+ reaps the report-backs it left undelivered across every inbox — they
367
+ accumulate. This scans all pending queues for that sender and clears them:
368
+ load-bearing kinds (``done``/``request``/``escalation``) are dead-lettered
369
+ (which escalates via the owner-email path so the loss is never silent); the
370
+ rest are dropped. Passive (``ingest``) messages are never auto-delivered, so
371
+ they're left for the recipient to pull. Returns ``{dead, dropped}`` counts.
372
+ """
373
+ dead = dropped = 0
374
+ if not INBOX_ROOT.exists():
375
+ return {"dead": dead, "dropped": dropped}
376
+
377
+ # Group this sender's pending files by recipient so each inbox is mutated
378
+ # under its per-session drain lock — serializing against an in-flight
379
+ # flush_session. Without it a kill landing mid-delivery could dead-letter +
380
+ # email "never delivered" for a message that WAS just delivered.
381
+ by_recipient: dict[str, list[Path]] = {}
382
+ for path in INBOX_ROOT.rglob("*.json"):
383
+ parts = path.relative_to(INBOX_ROOT).parts
384
+ if any(p in _RESERVED_DIRS for p in parts[:-1]):
385
+ continue # skip dead/ sent/ ingest/ .lock/
386
+ msg = _read_message(path)
387
+ if msg is None or msg.sender != sender:
388
+ continue
389
+ recipient = "/".join(parts[:-1])
390
+ if recipient:
391
+ by_recipient.setdefault(recipient, []).append(path)
392
+
393
+ for recipient, paths in by_recipient.items():
394
+ lock = _acquire_lock(recipient)
395
+ if lock is None:
396
+ # A flush is draining this inbox right now — its messages are being
397
+ # delivered, not lost. Skip GC for this recipient this round.
398
+ continue
399
+ try:
400
+ for path in paths:
401
+ if not path.exists():
402
+ continue # delivered + unlinked just before we locked
403
+ msg = _read_message(path)
404
+ if msg is None or msg.sender != sender:
405
+ continue
406
+ if msg.kind in ESCALATE_KINDS:
407
+ msg.dead_ts = _now_ms()
408
+ msg.reason = "sender_exited"
409
+ target = dead_dir(msg.to or "unknown") / path.name
410
+ try:
411
+ target.parent.mkdir(parents=True, exist_ok=True)
412
+ _write_message(target, msg)
413
+ path.unlink(missing_ok=True)
414
+ _log_event(
415
+ "dead_letter", id=msg.id, to=msg.to, kind=msg.kind,
416
+ attempts=msg.attempts, reason="sender_exited",
417
+ )
418
+ dead += 1
419
+ _escalate_dead_letter(msg, "sender_exited")
420
+ except OSError:
421
+ pass
422
+ else:
423
+ try:
424
+ path.unlink()
425
+ dropped += 1
426
+ except OSError:
427
+ pass
428
+ finally:
429
+ _release_lock(lock)
430
+
431
+ if dead or dropped:
432
+ _log_event("gc_sender", sender=sender, dead=dead, dropped=dropped)
433
+ return {"dead": dead, "dropped": dropped}
434
+
435
+
296
436
  # =============================================================================
297
437
  # Enqueue + broadcast
298
438
  # =============================================================================
@@ -321,7 +461,7 @@ def resolve_targets(to: str, sender: "str | None") -> list[str]:
321
461
  """
322
462
  if to == BROADCAST_TOKEN:
323
463
  return [s for s in _live_agent_sessions() if s != sender]
324
- return [to]
464
+ return [_validate_session(to)]
325
465
 
326
466
 
327
467
  def enqueue(
@@ -494,12 +634,52 @@ def _bump_attempts(messages: list[Message], reason: str = "") -> int:
494
634
  return dead
495
635
 
496
636
 
497
- def flush_session(session: str) -> dict:
637
+ def _dedup_landed(session: str, messages: list[Message]) -> list[Message]:
638
+ """Consume (unlink) every message whose render() is already on scrollback.
639
+
640
+ The load-bearing #621 fix. ``safe_deliver`` confirms submission by polling
641
+ the input box back to empty; under host load that confirm false-negatives
642
+ even though the paste *landed* and the recipient saw it. Retaining a landed
643
+ message re-injects it on every idle tick — forever. So before (and after) a
644
+ paste we check the recipient's scrollback per-message: any message whose own
645
+ first-line fragment is visible has demonstrably landed → unlink it. Only
646
+ truly-absent messages stay pending.
647
+
648
+ Per-message keying (not the coalesced blob) so a partial landing consumes
649
+ exactly the visible subset. A strict fragment check (never the generic
650
+ ``"[Pasted text"`` placeholder fallback) so a stray placeholder can't mark
651
+ every message delivered. A message that scrolled past the 200-line window
652
+ reads as not-visible → kept (safe direction: retry, never silent-drop).
653
+ """
654
+ from .session_ready import message_on_scrollback, scrollback
655
+
656
+ capture = scrollback(session, 0)
657
+ consumed: list[Message] = []
658
+ for msg in messages:
659
+ if msg.path is None:
660
+ continue
661
+ if message_on_scrollback(capture, msg.render()):
662
+ msg.path.unlink(missing_ok=True)
663
+ consumed.append(msg)
664
+ if consumed:
665
+ _log_event(
666
+ "delivered_dedup", to=session, count=len(consumed),
667
+ kinds=[m.kind for m in consumed],
668
+ )
669
+ return consumed
670
+
671
+
672
+ def flush_session(session: str, force: bool = False) -> dict:
498
673
  """Attempt to drain one session's inbox now.
499
674
 
500
675
  Delivers oldest-first, coalescing all queued messages into a single paste
501
676
  (one submit) when the box is empty. On any refusal the messages stay put,
502
677
  their ``attempts`` bump, and over the cap they dead-letter. Never raises.
678
+
679
+ *force* (the ``msg flush --force`` escape hatch) bypasses the empty-box /
680
+ busy gate and pastes regardless — for an operator un-wedging a stuck queue,
681
+ accepting that it may land mid-draft. The ``safe_deliver`` safety guards
682
+ (gone / parked / non-agent / live-dialog) are never bypassed.
503
683
  """
504
684
  from . import prompt_router
505
685
 
@@ -511,42 +691,74 @@ def flush_session(session: str) -> dict:
511
691
  if not messages:
512
692
  return {"session": session, "delivered": 0, "deferred": False, "reason": "empty"}
513
693
 
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
- }
694
+ pre_consumed = 0
695
+ if not force:
696
+ # Collision guard FIRST (cheap, and refuses dialogs/busy too via None).
697
+ # But first: a prior tick may have LANDED these and false-negatived the
698
+ # confirm (#621). If they're already on scrollback, consume them now
699
+ # instead of waiting for the box to free up to re-paste a duplicate.
700
+ consumed = _dedup_landed(session, messages)
701
+ if consumed:
702
+ pre_consumed = len(consumed)
703
+ messages = [m for m in messages if m.path is not None and m.path.exists()]
704
+ if not messages:
705
+ return {
706
+ "session": session, "delivered": pre_consumed,
707
+ "deferred": False, "reason": "delivered",
708
+ }
709
+
710
+ visible = prompt_router.capture(session, 0)
711
+ box_content = prompt_router.input_box_content(visible)
712
+
713
+ if box_content is None:
714
+ # Target is busy (input box not located). Defer.
715
+ dead = _bump_attempts(messages, "target_busy")
716
+ _log_event("deferred", to=session, count=len(messages), reason="target_busy")
717
+ return {
718
+ "session": session, "delivered": pre_consumed, "deferred": True,
719
+ "reason": "target_busy", "dead": dead,
720
+ }
721
+
722
+ if box_content != "":
723
+ # Box is not empty. We never bypass this to protect human drafts. But
724
+ # the "queued messages" placeholder is a BUSY signal, not a draft:
725
+ # defer WITHOUT penalty (like target_busy) so a generating-with-queued
726
+ # session doesn't burn report-backs toward dead-letter. Either way we
727
+ # never paste — only the penalty decision differs.
728
+ placeholder = prompt_router.is_queued_placeholder(box_content)
729
+ reason = "queued_placeholder" if placeholder else "box_not_empty"
730
+ dead = _bump_attempts(messages, reason)
731
+ _log_event("deferred", to=session, count=len(messages), reason=reason)
732
+ return {
733
+ "session": session, "delivered": pre_consumed, "deferred": True,
734
+ "reason": reason, "dead": dead,
735
+ }
541
736
 
542
737
  rendered = "\n".join(m.render() for m in messages)
543
738
  delivered, reason = prompt_router.safe_deliver(session, 0, rendered)
544
739
  if not delivered:
545
- dead = _bump_attempts(messages, reason)
546
- _log_event("deferred", to=session, count=len(messages), reason=reason)
740
+ # delivery_unverified means the box-cleared confirm failed — but the
741
+ # paste may have LANDED. Consume any message now visible on scrollback
742
+ # (idempotent delivery) so a false-negative can't cause re-injection.
743
+ # Other reasons (gone/parked/non-agent/dialog) never pasted, so there's
744
+ # nothing to dedup.
745
+ consumed = (
746
+ _dedup_landed(session, messages)
747
+ if reason == "delivery_unverified"
748
+ else []
749
+ )
750
+ consumed_ids = {m.id for m in consumed}
751
+ remaining = [m for m in messages if m.id not in consumed_ids]
752
+ if not remaining:
753
+ return {
754
+ "session": session, "delivered": pre_consumed + len(consumed),
755
+ "deferred": False, "reason": "delivered",
756
+ }
757
+ dead = _bump_attempts(remaining, reason)
758
+ _log_event("deferred", to=session, count=len(remaining), reason=reason)
547
759
  return {
548
- "session": session, "delivered": 0, "deferred": True,
549
- "reason": reason, "dead": dead,
760
+ "session": session, "delivered": pre_consumed + len(consumed),
761
+ "deferred": True, "reason": reason, "dead": dead,
550
762
  }
551
763
 
552
764
  for msg in messages:
@@ -557,7 +769,7 @@ def flush_session(session: str) -> dict:
557
769
  kinds=[m.kind for m in messages],
558
770
  )
559
771
  return {
560
- "session": session, "delivered": len(messages),
772
+ "session": session, "delivered": pre_consumed + len(messages),
561
773
  "deferred": False, "reason": "delivered",
562
774
  }
563
775
  except Exception as exc: # draining must never break the watchdog