agentwire-dev 1.53.0__tar.gz → 1.55.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 (474) hide show
  1. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/PKG-INFO +1 -1
  2. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/__init__.py +1 -1
  3. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/__main__.py +2 -1
  4. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/config.py +4 -0
  5. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/config_cli.py +1 -0
  6. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/core.py +26 -2
  7. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/bash-tool-damage-control.py +197 -5
  8. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/edit-tool-damage-control.py +197 -5
  9. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/mcp-tool-damage-control.py +197 -5
  10. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/read-tool-damage-control.py +197 -5
  11. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/rules/git.yaml +14 -0
  12. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/write-tool-damage-control.py +197 -5
  13. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/inbox.py +103 -8
  14. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_msg.py +20 -6
  15. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_worktree.py +30 -4
  16. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/msg_cli.py +35 -8
  17. agentwire_dev-1.55.0/agentwire/palette_cli.py +188 -0
  18. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/pane_manager.py +8 -1
  19. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/prompt_router.py +87 -2
  20. agentwire_dev-1.55.0/agentwire/routes/palette.py +79 -0
  21. agentwire_dev-1.55.0/agentwire/safety/_core.py +1336 -0
  22. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/scheduler/__init__.py +1 -0
  23. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/scheduler/dispatch.py +120 -3
  24. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/scheduler/schedule.py +12 -2
  25. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/server.py +28 -0
  26. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/session_cli.py +37 -9
  27. agentwire_dev-1.55.0/agentwire/session_ready.py +641 -0
  28. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/command-palette.js +106 -2
  29. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/desktop.js +22 -29
  30. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/shortcuts.js +1 -1
  31. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/voice_status.py +52 -5
  32. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/sessions/messaging.md +68 -9
  33. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/conftest.py +40 -0
  34. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/integration/test_send_verified_tmux.py +120 -28
  35. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_cli_commands.py +77 -0
  36. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_control_plane_protection.py +35 -0
  37. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_damage_control_hooks.py +91 -0
  38. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_inbox.py +165 -0
  39. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_mcp_tools_args.py +63 -0
  40. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_msg_cli.py +96 -2
  41. agentwire_dev-1.55.0/tests/unit/test_palette_cli.py +133 -0
  42. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_portal_api.py +26 -0
  43. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_prompt_router.py +136 -0
  44. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_routes_snapshot.py +3 -0
  45. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_scheduler.py +124 -0
  46. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_session_ready.py +407 -49
  47. agentwire_dev-1.55.0/tests/unit/test_unattended_env_isolation.py +90 -0
  48. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_voice_status.py +68 -0
  49. agentwire_dev-1.55.0/tests/unit/test_window_cycling_binding.py +65 -0
  50. agentwire_dev-1.53.0/agentwire/safety/_core.py +0 -1144
  51. agentwire_dev-1.53.0/agentwire/session_ready.py +0 -413
  52. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/.agentwire.yml.example +0 -0
  53. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/.claude/commands/handoff.md +0 -0
  54. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/.claude/skills/wiki/SKILL.md +0 -0
  55. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/.github/FUNDING.yml +0 -0
  56. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  57. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  58. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/.github/ISSUE_TEMPLATE/question.md +0 -0
  59. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  60. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/.github/workflows/pytest.yml +0 -0
  61. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/.github/workflows/ruff.yml +0 -0
  62. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/.github/workflows/security.yml +0 -0
  63. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/.github/workflows/tts-smoke.yml +0 -0
  64. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/.gitignore +0 -0
  65. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/AGENTS.md +0 -0
  66. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/CODE_OF_CONDUCT.md +0 -0
  67. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/CONTRIBUTING.md +0 -0
  68. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/Dockerfile.local +0 -0
  69. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/LICENSE +0 -0
  70. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/README.md +0 -0
  71. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/RELEASING.md +0 -0
  72. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/SECURITY.md +0 -0
  73. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/SPONSORS.md +0 -0
  74. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/agents/__init__.py +0 -0
  75. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/agents/base.py +0 -0
  76. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/agents/tmux.py +0 -0
  77. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/cached_status.py +0 -0
  78. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/channels/__init__.py +0 -0
  79. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/channels/base.py +0 -0
  80. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/channels/email.py +0 -0
  81. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/channels/push.py +0 -0
  82. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/channels/quo.py +0 -0
  83. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/channels_cli.py +0 -0
  84. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/completion.py +0 -0
  85. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/council/__init__.py +0 -0
  86. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/council/cli.py +0 -0
  87. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/council/inbox.py +0 -0
  88. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/council/state.py +0 -0
  89. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/council/view.py +0 -0
  90. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/devices.py +0 -0
  91. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/diff_cli.py +0 -0
  92. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/doctor_cli.py +0 -0
  93. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/doctor_voice.py +0 -0
  94. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/ensure_cli.py +0 -0
  95. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/fetch.py +0 -0
  96. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/handoff/__init__.py +0 -0
  97. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/handoff/git_state.py +0 -0
  98. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/handoff/instructions.py +0 -0
  99. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/handoff/parser.py +0 -0
  100. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/handoff/renderer.py +0 -0
  101. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/handoff/schema.py +0 -0
  102. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/handoff_cli.py +0 -0
  103. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/history.py +0 -0
  104. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/history_cli.py +0 -0
  105. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/__init__.py +0 -0
  106. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/agentwire-permission.sh +0 -0
  107. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/__init__.py +0 -0
  108. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/audit_logger.py +0 -0
  109. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/rules/agentwire.yaml +0 -0
  110. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/rules/aws.yaml +0 -0
  111. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/rules/cloud-hosting.yaml +0 -0
  112. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/rules/containers.yaml +0 -0
  113. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/rules/control-plane.yaml +0 -0
  114. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/rules/core.yaml +0 -0
  115. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/rules/databases.yaml +0 -0
  116. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/rules/firebase.yaml +0 -0
  117. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/rules/gcp.yaml +0 -0
  118. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/rules/gws.yaml +0 -0
  119. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/rules/infrastructure.yaml +0 -0
  120. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/rules/outbound.yaml +0 -0
  121. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/rules/publish.yaml +0 -0
  122. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/rules/remote.yaml +0 -0
  123. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/idle-handler.sh +0 -0
  124. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/queue-processor.sh +0 -0
  125. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks_cli.py +0 -0
  126. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/limits_cli.py +0 -0
  127. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/listen.py +0 -0
  128. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/locking.py +0 -0
  129. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/machine_cli.py +0 -0
  130. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_channels.py +0 -0
  131. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_cli.py +0 -0
  132. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_core.py +0 -0
  133. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_council.py +0 -0
  134. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_desktop.py +0 -0
  135. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_handoff.py +0 -0
  136. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_history.py +0 -0
  137. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_listen.py +0 -0
  138. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_lock.py +0 -0
  139. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_machine.py +0 -0
  140. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_notify.py +0 -0
  141. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_pane.py +0 -0
  142. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_projects.py +0 -0
  143. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_scheduler.py +0 -0
  144. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_scratchpad.py +0 -0
  145. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_server.py +0 -0
  146. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_services.py +0 -0
  147. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_session.py +0 -0
  148. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_status.py +0 -0
  149. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_task.py +0 -0
  150. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_tunnels.py +0 -0
  151. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_voice.py +0 -0
  152. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_voiceclone.py +0 -0
  153. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_wiki.py +0 -0
  154. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/network.py +0 -0
  155. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/notify_cli.py +0 -0
  156. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/onboarding.py +0 -0
  157. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/pane_cli.py +0 -0
  158. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/portal_cli.py +0 -0
  159. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/project_config.py +0 -0
  160. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/projects.py +0 -0
  161. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/prompts_cli.py +0 -0
  162. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/push_cli.py +0 -0
  163. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/push_store.py +0 -0
  164. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/repo_cli.py +0 -0
  165. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/research.py +0 -0
  166. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/__init__.py +0 -0
  167. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/agentwire.md +0 -0
  168. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/anchor.md +0 -0
  169. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/chatbot.md +0 -0
  170. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/contributor.md +0 -0
  171. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/correspondent.md +0 -0
  172. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/council-brain.md +0 -0
  173. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/council-conscience.md +0 -0
  174. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/council-critic.md +0 -0
  175. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/council-devils-advocate.md +0 -0
  176. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/council-gut.md +0 -0
  177. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/council-historian.md +0 -0
  178. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/council-member.md +0 -0
  179. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/council-orchestrator.md +0 -0
  180. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/init.md +0 -0
  181. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/notifications.md +0 -0
  182. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/orchestrator.md +0 -0
  183. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/soul.md +0 -0
  184. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/task-runner.md +0 -0
  185. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/voice.md +0 -0
  186. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/worker.md +0 -0
  187. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/worktree-session.md +0 -0
  188. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles_cli.py +0 -0
  189. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/routes/__init__.py +0 -0
  190. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/routes/artifacts.py +0 -0
  191. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/routes/config.py +0 -0
  192. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/routes/council.py +0 -0
  193. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/routes/desktop.py +0 -0
  194. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/routes/history.py +0 -0
  195. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/routes/machines.py +0 -0
  196. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/routes/notify.py +0 -0
  197. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/routes/permission.py +0 -0
  198. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/routes/projects.py +0 -0
  199. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/routes/push.py +0 -0
  200. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/routes/safety.py +0 -0
  201. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/routes/scheduler.py +0 -0
  202. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/routes/scratchpad.py +0 -0
  203. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/routes/sessions.py +0 -0
  204. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/routes/sessions_admin.py +0 -0
  205. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/routes/voice.py +0 -0
  206. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/safety/__init__.py +0 -0
  207. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/safety_cli.py +0 -0
  208. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/safety_commands.py +0 -0
  209. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/scheduler/gates.py +0 -0
  210. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/scheduler/loop.py +0 -0
  211. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/scheduler/models.py +0 -0
  212. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/scheduler/report.py +0 -0
  213. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/scheduler/state.py +0 -0
  214. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/scheduler_cli.py +0 -0
  215. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/scratchpad.py +0 -0
  216. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/security.py +0 -0
  217. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/send_cli.py +0 -0
  218. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/services.py +0 -0
  219. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/session_context.py +0 -0
  220. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/ssh.py +0 -0
  221. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/agentwire-splash-logo-layers--agentwire-text.png +0 -0
  222. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/agentwire-splash-logo-layers--echo-claw-fg.png +0 -0
  223. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/agentwire-splash-logo-layers--echo.png +0 -0
  224. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/agentwire-splash-logo-layers--telephone.png +0 -0
  225. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/agentwire-splash-logo-layers--tree.png +0 -0
  226. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/announcements.json +0 -0
  227. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/apple-touch-icon.png +0 -0
  228. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/css/desktop.css +0 -0
  229. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/css/mobile.css +0 -0
  230. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/favicon-32.png +0 -0
  231. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/favicon-64.png +0 -0
  232. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/icons/sessions/bear.webp +0 -0
  233. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/icons/sessions/cat.webp +0 -0
  234. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/icons/sessions/crown.webp +0 -0
  235. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/icons/sessions/custom/agentwire-portal.webp +0 -0
  236. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/icons/sessions/custom/agentwire-tts.webp +0 -0
  237. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/icons/sessions/custom/agentwire.webp +0 -0
  238. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/icons/sessions/deer.webp +0 -0
  239. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/icons/sessions/drone.webp +0 -0
  240. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/icons/sessions/eagle.webp +0 -0
  241. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/icons/sessions/fox.webp +0 -0
  242. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/icons/sessions/hawk.webp +0 -0
  243. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/icons/sessions/horse.webp +0 -0
  244. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/icons/sessions/lion.webp +0 -0
  245. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/icons/sessions/rabbit.webp +0 -0
  246. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/icons/sessions/robot.webp +0 -0
  247. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/icons/sessions/tiger.webp +0 -0
  248. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/icons/sessions/wolf.webp +0 -0
  249. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/img/icon-192.png +0 -0
  250. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/img/icon-512.png +0 -0
  251. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/.gitkeep +0 -0
  252. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/announcement-modal.js +0 -0
  253. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/api.js +0 -0
  254. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/artifact-window.js +0 -0
  255. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/collage.js +0 -0
  256. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/council-window.js +0 -0
  257. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/dead-key-suppressor.js +0 -0
  258. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/desktop-manager.js +0 -0
  259. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/help-modal.js +0 -0
  260. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/icon-manager.js +0 -0
  261. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/mobile.js +0 -0
  262. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/notification-prefs.js +0 -0
  263. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/notifications-panel.js +0 -0
  264. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/ptt.js +0 -0
  265. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/push-client.js +0 -0
  266. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/review-window.js +0 -0
  267. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/safety-shared.js +0 -0
  268. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/safety-window.js +0 -0
  269. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/scratchpad.js +0 -0
  270. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/service-classification.js +0 -0
  271. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/session-id.js +0 -0
  272. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/session-window.js +0 -0
  273. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/sidebar/artifacts-section.js +0 -0
  274. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/sidebar/config-section.js +0 -0
  275. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/sidebar/council-section.js +0 -0
  276. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/sidebar/machines-section.js +0 -0
  277. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/sidebar/projects-section.js +0 -0
  278. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/sidebar/safety-section.js +0 -0
  279. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/sidebar/scheduler-section.js +0 -0
  280. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/sidebar/services-section.js +0 -0
  281. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/sidebar/sessions-section.js +0 -0
  282. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/sidebar.js +0 -0
  283. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/terminal-font-prefs.js +0 -0
  284. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/tile-manager.js +0 -0
  285. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/toast.js +0 -0
  286. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/token-modal.js +0 -0
  287. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/utils/ansi.js +0 -0
  288. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/utils/swipe.js +0 -0
  289. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/voice/autosend-prefs.js +0 -0
  290. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/voice/browser-stt.js +0 -0
  291. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/voice/browser-tts.js +0 -0
  292. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/voice/jargon.js +0 -0
  293. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/voice/prompt.js +0 -0
  294. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/winbox.bundle.min.js +0 -0
  295. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/manifest.webmanifest +0 -0
  296. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/service-worker.js +0 -0
  297. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/stt/__init__.py +0 -0
  298. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/stt/base.py +0 -0
  299. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/stt/cloud.py +0 -0
  300. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/stt/engine.py +0 -0
  301. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/stt/local.py +0 -0
  302. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/stt/server_backend.py +0 -0
  303. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/stt/stt_server.py +0 -0
  304. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/system_cli.py +0 -0
  305. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tasks.py +0 -0
  306. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/templates/__init__.py +0 -0
  307. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/templates/base.html +0 -0
  308. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/templates/desktop.html +0 -0
  309. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/templates/email_notification.html +0 -0
  310. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/templates/handoff/show-the-story.html.j2 +0 -0
  311. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/templates/handoff/theme.css.j2 +0 -0
  312. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/templates/mobile.html +0 -0
  313. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/templates/pair.html +0 -0
  314. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/templates/tmux.conf +0 -0
  315. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/templating.py +0 -0
  316. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tooldefs/aws.yaml +0 -0
  317. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tooldefs/docker.yaml +0 -0
  318. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tooldefs/gcp.yaml +0 -0
  319. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tooldefs/gh.yaml +0 -0
  320. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tooldefs/git.yaml +0 -0
  321. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tooldefs/gws.yaml +0 -0
  322. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tooldefs/kubectl.yaml +0 -0
  323. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tooldefs/npm.yaml +0 -0
  324. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tooldefs/terraform.yaml +0 -0
  325. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tooldefs/uv.yaml +0 -0
  326. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tts/__init__.py +0 -0
  327. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tts/audio.py +0 -0
  328. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tts/base.py +0 -0
  329. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tts/engines/__init__.py +0 -0
  330. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tts/engines/chatterbox.py +0 -0
  331. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tts/engines/kokoro.py +0 -0
  332. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tts/engines/zonos.py +0 -0
  333. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tts/kokoro_server.py +0 -0
  334. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tts/local.py +0 -0
  335. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tts/registry.py +0 -0
  336. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tts_cli.py +0 -0
  337. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tts_server.py +0 -0
  338. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tunnels.py +0 -0
  339. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tunnels_cli.py +0 -0
  340. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/usage_limit.py +0 -0
  341. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/utils/__init__.py +0 -0
  342. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/utils/chunker.py +0 -0
  343. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/utils/event_log.py +0 -0
  344. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/utils/file_io.py +0 -0
  345. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/utils/paths.py +0 -0
  346. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/utils/speech.py +0 -0
  347. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/utils/subprocess.py +0 -0
  348. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/validation.py +0 -0
  349. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/voiceclone.py +0 -0
  350. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/wiki.py +0 -0
  351. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/wiki_audit.py +0 -0
  352. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/wiki_cli.py +0 -0
  353. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/worktree.py +0 -0
  354. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/worktree_registry.py +0 -0
  355. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/decisions/obsidian-second-brain.md +0 -0
  356. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/logo.png +0 -0
  357. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/INDEX.md +0 -0
  358. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/architecture.md +0 -0
  359. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/briefing-mode.md +0 -0
  360. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/communication/channels.md +0 -0
  361. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/communication/hammerspoon.md +0 -0
  362. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/communication/handoff.md +0 -0
  363. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/concepts.md +0 -0
  364. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/council.md +0 -0
  365. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/deployment/remote-access.md +0 -0
  366. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/deployment/remote-machines.md +0 -0
  367. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/glossary.md +0 -0
  368. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/integrations/gws-google-workspace-cli.md +0 -0
  369. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/internals/damage-control.md +0 -0
  370. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/internals/parallel-refactor.md +0 -0
  371. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/internals/portal.md +0 -0
  372. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/internals/shell-escaping.md +0 -0
  373. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/internals/troubleshooting.md +0 -0
  374. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/internals/window-collage.md +0 -0
  375. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/quickstart.md +0 -0
  376. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/research/briefing-mode-feasibility.md +0 -0
  377. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/research/orchestration-transport-alternatives.md +0 -0
  378. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/scheduling/scheduled-workloads.md +0 -0
  379. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/security/damage-control-hardening.md +0 -0
  380. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/security/pip-audit.md +0 -0
  381. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/security/remote-access-hardening.md +0 -0
  382. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/security/secrets.md +0 -0
  383. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/services.md +0 -0
  384. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/sessions/claude-code-auto-mode.md +0 -0
  385. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/sessions/pi.md +0 -0
  386. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/sessions/prompt-routing.md +0 -0
  387. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/sessions/window-sizing.md +0 -0
  388. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/sessions/worktree-sessions.md +0 -0
  389. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/usage-limit-recovery.md +0 -0
  390. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/voice/shim-contract.md +0 -0
  391. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/voice/stt-cloud.md +0 -0
  392. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/voice/stt-self-hosted.md +0 -0
  393. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/voice/tts-self-hosted.md +0 -0
  394. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/pyproject.toml +0 -0
  395. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/e2e/test_portal_ui.py +0 -0
  396. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/fixtures/sample_agentwire.yml +0 -0
  397. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/fixtures/sample_config.yaml +0 -0
  398. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/fixtures/sample_scheduler.yaml +0 -0
  399. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/integration/test_council_portal.py +0 -0
  400. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/integration/test_scheduler_board.py +0 -0
  401. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/integration/test_server_websockets.py +0 -0
  402. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/integration/test_worktree_cmd.py +0 -0
  403. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_announcements.py +0 -0
  404. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_build_agent_command.py +0 -0
  405. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_channels.py +0 -0
  406. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_cli_output.py +0 -0
  407. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_cli_smoke.py +0 -0
  408. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_config.py +0 -0
  409. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_config_cli.py +0 -0
  410. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_council_cli.py +0 -0
  411. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_council_inbox.py +0 -0
  412. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_council_state.py +0 -0
  413. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_council_view.py +0 -0
  414. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_damage_control_bypass.py +0 -0
  415. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_damage_control_sync.py +0 -0
  416. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_devices.py +0 -0
  417. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_diff_cli.py +0 -0
  418. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_doctor_voice.py +0 -0
  419. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_event_log.py +0 -0
  420. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_file_io.py +0 -0
  421. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_handoff_git_state.py +0 -0
  422. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_handoff_instructions.py +0 -0
  423. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_handoff_parser.py +0 -0
  424. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_handoff_renderer.py +0 -0
  425. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_history.py +0 -0
  426. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_hooks_install.py +0 -0
  427. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_idle_handler.py +0 -0
  428. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_idle_nag.py +0 -0
  429. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_init_command.py +0 -0
  430. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_install_parity.py +0 -0
  431. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_limits_tick_isolation.py +0 -0
  432. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_locking.py +0 -0
  433. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_mcp_server.py +0 -0
  434. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_notify_cli.py +0 -0
  435. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_onboarding_reinit.py +0 -0
  436. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_pip_environment.py +0 -0
  437. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_project_config.py +0 -0
  438. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_push.py +0 -0
  439. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_rebuild_guard.py +0 -0
  440. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_repo_cli.py +0 -0
  441. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_roles.py +0 -0
  442. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_safety_commands.py +0 -0
  443. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_safety_disabled_rules.py +0 -0
  444. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_safety_escape_hatch.py +0 -0
  445. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_safety_heal.py +0 -0
  446. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_safety_kill_switch.py +0 -0
  447. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_scheduler_parsing.py +0 -0
  448. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_scratchpad.py +0 -0
  449. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_security.py +0 -0
  450. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_server_async.py +0 -0
  451. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_server_pure.py +0 -0
  452. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_services.py +0 -0
  453. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_session_context.py +0 -0
  454. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_shared_dir_guard.py +0 -0
  455. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_skill_install.py +0 -0
  456. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_speech_tags.py +0 -0
  457. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_ssh.py +0 -0
  458. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_stt_backend.py +0 -0
  459. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_stt_cloud.py +0 -0
  460. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_stt_engine.py +0 -0
  461. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_task_cli.py +0 -0
  462. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_tasks.py +0 -0
  463. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_templating.py +0 -0
  464. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_terminal_resize.py +0 -0
  465. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_tmux_template.py +0 -0
  466. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_tts_engine_resolution.py +0 -0
  467. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_tts_local.py +0 -0
  468. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_tunnels_probe.py +0 -0
  469. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_usage_limit.py +0 -0
  470. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_validation.py +0 -0
  471. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_wiki.py +0 -0
  472. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_wiki_audit.py +0 -0
  473. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_worktree.py +0 -0
  474. {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_worktree_registry.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentwire-dev
3
- Version: 1.53.0
3
+ Version: 1.55.0
4
4
  Summary: Multi-session voice web interface for AI coding agents
5
5
  Project-URL: Homepage, https://agentwire.dev
6
6
  Project-URL: Repository, https://github.com/dotdevdotdev/agentwire-dev
@@ -1,3 +1,3 @@
1
1
  """AgentWire - Multi-session voice web interface for AI coding agents."""
2
2
 
3
- __version__ = "1.53.0"
3
+ __version__ = "1.55.0"
@@ -191,7 +191,7 @@ Command Categories:
191
191
  from . import scheduler_cli, ensure_cli
192
192
  from . import doctor_cli, history_cli, machine_cli, safety_cli
193
193
  from . import handoff_cli, hooks_cli, mcp_cli, roles_cli, tunnels_cli
194
- from . import notify_cli, push_cli, repo_cli, system_cli, wiki_cli
194
+ from . import notify_cli, palette_cli, push_cli, repo_cli, system_cli, wiki_cli
195
195
  from .council import cli as council_cli
196
196
 
197
197
  _REGISTRARS = [ # noqa: N806 # registry constant; Phase 1 of #495 appends here
@@ -223,6 +223,7 @@ Command Categories:
223
223
  push_cli.register_push_parser,
224
224
  repo_cli.register_repo_parser,
225
225
  config_cli.register_config_parser,
226
+ palette_cli.register_palette_parser,
226
227
  ]
227
228
  for _reg in _REGISTRARS:
228
229
  _reg(subparsers)
@@ -431,6 +431,10 @@ class SchedulerConfig:
431
431
  session_create_timeout: int = 30
432
432
  max_loop_sleep: int = 60
433
433
  dispatch_cooldown: int = 60
434
+ # Watchdog ceiling for a single dispatch (seconds). A hung `ensure`
435
+ # (e.g. an unrecognized usage-limit dialog) otherwise holds the single
436
+ # dispatch slot forever and starves the whole board (#677). 0 disables.
437
+ dispatch_max_runtime: int = 14400
434
438
  # Crash-loop guard: a board that won't parse / has no tasks backs off
435
439
  # exponentially from `error_backoff_base` up to `error_backoff_max`
436
440
  # instead of tight-looping (#449).
@@ -28,6 +28,7 @@ EXECUTION_PLANE_PREFIXES = (
28
28
  "pi.",
29
29
  "hooks.",
30
30
  "dev.",
31
+ "palette.", # user palette `run` templates feed the shell (#676)
31
32
  )
32
33
 
33
34
  # Value shapes that are command-shaped regardless of section.
@@ -78,12 +78,36 @@ class AgentCommand:
78
78
  _UNATTENDED_ENV_KEYS = ("AGENTWIRE_UNATTENDED", "AGENTWIRE_UNATTENDED_ALLOW")
79
79
 
80
80
 
81
+ def _capture_unattended_env() -> dict[str, str]:
82
+ """Pop the unattended marker OUT of this process's environment.
83
+
84
+ The marker must reach new sessions only via the deliberate
85
+ ``tmux new-session -e K=V`` path (``_with_unattended_env`` below). If it
86
+ stays in ``os.environ``, a ``tmux`` client we spawn while no server is
87
+ running boots the shared tmux server WITH the marker in its process env,
88
+ and from then on every session that server creates — interactive human
89
+ sessions included — inherits it and gets falsely treated as unattended
90
+ (#674). Capturing at import time preserves intended propagation while
91
+ guaranteeing no child process of the CLI can inherit the raw var.
92
+ """
93
+ captured: dict[str, str] = {}
94
+ for key in _UNATTENDED_ENV_KEYS:
95
+ val = os.environ.pop(key, None)
96
+ if val:
97
+ captured[key] = val
98
+ return captured
99
+
100
+
101
+ _UNATTENDED_ENV = _capture_unattended_env()
102
+
103
+
81
104
  def _with_unattended_env(env: dict[str, str]) -> dict[str, str]:
82
105
  """Propagate the unattended marker into a session being created.
83
106
 
84
107
  The scheduler is the single place that decides a dispatch is unattended —
85
108
  it seeds ``AGENTWIRE_UNATTENDED[=1]`` (and any per-task
86
- ``AGENTWIRE_UNATTENDED_ALLOW``) into the dispatch subprocess environment.
109
+ ``AGENTWIRE_UNATTENDED_ALLOW``) into the dispatch subprocess environment,
110
+ captured here at import (see ``_capture_unattended_env``).
87
111
  Every session-creation path funnels its env through here on the way to
88
112
  ``tmux new-session -e K=V``, so the marker lands in the new session BEFORE
89
113
  the agent launches and the damage-control hook can read it. A child session
@@ -94,7 +118,7 @@ def _with_unattended_env(env: dict[str, str]) -> dict[str, str]:
94
118
  """
95
119
  merged = dict(env)
96
120
  for key in _UNATTENDED_ENV_KEYS:
97
- val = os.environ.get(key)
121
+ val = _UNATTENDED_ENV.get(key)
98
122
  if val and key not in merged:
99
123
  merged[key] = val
100
124
  return merged
@@ -121,6 +121,25 @@ READ_ONLY_BLOCKED = (
121
121
 
122
122
  NO_DELETE_BLOCKED = DELETE_PATTERNS
123
123
 
124
+ # Interpreter programs can write a file without any shell-level write token:
125
+ # ``python3 -c 'open(p, "w")...'``, a heredoc piped into ``python -``, or
126
+ # ``echo 'open(p,"w")' | python3``. The program text is opaque to static
127
+ # matching, so for PROTECTED control-plane paths we fail closed: an interpreter
128
+ # invocation that takes inline code (-c/-e, combined forms like ``perl -pie``),
129
+ # a heredoc/stdin program, or has data piped into it, AND mentions a protected
130
+ # path anywhere in the command, is blocked (#678). Applied ONLY by
131
+ # ``check_protected_command`` — never to user readOnlyPaths rules — so a
132
+ # legitimate read via ``cat``/``grep`` stays allowed.
133
+ _INTERPRETERS = r'(?:python[\d.]*|perl|ruby|node|deno|bun|php)'
134
+ INTERPRETER_WRITE_PATTERNS = [
135
+ # interpreter ... -c/-e/... or stdin/heredoc marker, path anywhere after
136
+ (r'\b' + _INTERPRETERS +
137
+ r'\b[^\n|;&]*(?:\s-\S*[ceE]\b|\s+-\s|\s+-$|<<)(?s:.*){path}',
138
+ "interpreter program"),
139
+ # path anywhere before a pipe into an interpreter
140
+ (r'{path}(?s:.*)\|\s*' + _INTERPRETERS + r'\b', "pipe into interpreter"),
141
+ ]
142
+
124
143
 
125
144
  # ============================================================================
126
145
  # GLOB / PATH HELPERS
@@ -416,7 +435,11 @@ def check_path_patterns(
416
435
  Returns ``(matched, reason)`` where reason names the operation that triggered.
417
436
  """
418
437
  if is_glob_pattern(path):
419
- path_regex = glob_to_regex(path)
438
+ # Allow an optional (possibly quoted) directory prefix: ``*`` in the
439
+ # glob can't span ``/``, so without this a basename glob like
440
+ # ``*.agentwire.yml`` never matched an absolute target
441
+ # (``> /repo/.agentwire.yml`` sailed past every write pattern) (#678).
442
+ path_regex = r'["\']?(?:[^\s"\';|&<>]*/)?' + glob_to_regex(path)
420
443
  else:
421
444
  expanded = os.path.expanduser(path)
422
445
  if expanded == path:
@@ -484,6 +507,9 @@ def load_write_patterns_from_tooldefs(tooldefs_dir: Optional[Path]) -> List[Dict
484
507
  "pattern": regex,
485
508
  "reason": f"{tool_name}: {cmd_def.get('description', cmd_str)}",
486
509
  "ask": True,
510
+ # Command-prefix rule: match command position only, never
511
+ # quoted argument/message content (#675).
512
+ "anchored": True,
487
513
  }
488
514
  # An explicit ``id:`` on the tooldef command yields a stable
489
515
  # rule ID (e.g. ``git.push``) that survives description edits —
@@ -806,7 +832,10 @@ def check_protected_command(
806
832
  for path in _protected_path_patterns():
807
833
  for hay in haystacks:
808
834
  matched, reason = check_path_patterns(
809
- hay, path, READ_ONLY_BLOCKED, "protected control-plane path"
835
+ hay,
836
+ path,
837
+ READ_ONLY_BLOCKED + INTERPRETER_WRITE_PATTERNS,
838
+ "protected control-plane path",
810
839
  )
811
840
  if matched:
812
841
  op = _infer_operation_from_reason(reason)
@@ -918,6 +947,165 @@ def normalize_subcommands(command: str) -> Tuple[List[str], Optional[str]]:
918
947
  return subs, None
919
948
 
920
949
 
950
+ #
951
+ # CONTENT MASKING (#675)
952
+ #
953
+ # Tooldef rules are COMMAND-PREFIX rules (``\bgh\s+pr\s+comment\b``), but the
954
+ # haystacks above include quoted argument text — so a commit message that
955
+ # merely *mentions* "gh pr comment" false-matches the rule. Anchored rules are
956
+ # therefore matched against MASKED subcommands instead: quoted spans that can
957
+ # only be content (a quoted token containing whitespace — a message, an echo
958
+ # string, a heredoc body) are replaced with a placeholder, while quoted
959
+ # single words are kept (``"gh" pr comment`` is still a real command) and
960
+ # ``sh -c "…"`` payloads are recursively re-scanned as commands.
961
+
962
+ _MASK = "\x00"
963
+ _HEREDOC_RE = re.compile(r"<<-?\s*(['\"]?)([A-Za-z_]\w+)\1")
964
+ _SHELL_NAMES = {"sh", "bash", "zsh", "dash", "ksh"}
965
+ _ASSIGN_TOKEN_RE = re.compile(r"^[A-Za-z_]\w*=")
966
+
967
+
968
+ def _strip_heredoc_bodies(command: str) -> str:
969
+ """Remove heredoc body lines (``<<EOF … EOF``) — they are content."""
970
+ m = _HEREDOC_RE.search(command)
971
+ if not m:
972
+ return command
973
+ delim = m.group(2)
974
+ nl = command.find("\n", m.end())
975
+ if nl == -1:
976
+ return command
977
+ lines = command[nl + 1:].split("\n")
978
+ for i, line in enumerate(lines):
979
+ if line.strip() == delim:
980
+ rest = "\n".join(lines[i + 1:])
981
+ return command[:nl + 1] + _strip_heredoc_bodies(rest)
982
+ # Unterminated heredoc: everything after the marker is body.
983
+ return command[:nl + 1]
984
+
985
+
986
+ def _scan_masked_tokens(command: str) -> List[List[Tuple[str, bool]]]:
987
+ """Split ``command`` into subcommands of ``(token_text, fully_quoted)``.
988
+
989
+ Quotes/escapes are resolved into the token text (so ``g''h`` -> ``gh``);
990
+ ``fully_quoted`` is True when no character of the token was unquoted.
991
+ """
992
+ subs: List[List[Tuple[str, bool]]] = []
993
+ cur: List[Tuple[str, bool]] = []
994
+ buf: List[str] = []
995
+ has_unquoted = False
996
+ has_any = False
997
+
998
+ def end_token() -> None:
999
+ nonlocal buf, has_unquoted, has_any
1000
+ if has_any:
1001
+ cur.append(("".join(buf), not has_unquoted))
1002
+ buf, has_unquoted, has_any = [], False, False
1003
+
1004
+ def end_sub() -> None:
1005
+ nonlocal cur
1006
+ end_token()
1007
+ if cur:
1008
+ subs.append(cur)
1009
+ cur = []
1010
+
1011
+ i, n = 0, len(command)
1012
+ while i < n:
1013
+ c = command[i]
1014
+ if c == "'":
1015
+ j = command.find("'", i + 1)
1016
+ if j == -1:
1017
+ j = n
1018
+ buf.append(command[i + 1:j])
1019
+ has_any = True
1020
+ i = j + 1
1021
+ elif c == '"':
1022
+ j = i + 1
1023
+ out: List[str] = []
1024
+ while j < n and command[j] != '"':
1025
+ if command[j] == "\\" and j + 1 < n and command[j + 1] in '"\\$`':
1026
+ out.append(command[j + 1])
1027
+ j += 2
1028
+ else:
1029
+ out.append(command[j])
1030
+ j += 1
1031
+ buf.append("".join(out))
1032
+ has_any = True
1033
+ i = j + 1
1034
+ elif c == "\\" and i + 1 < n:
1035
+ buf.append(command[i + 1])
1036
+ has_unquoted = True
1037
+ has_any = True
1038
+ i += 2
1039
+ elif c in " \t":
1040
+ end_token()
1041
+ i += 1
1042
+ elif c in ";\n":
1043
+ end_sub()
1044
+ i += 1
1045
+ elif c in "&|":
1046
+ end_sub()
1047
+ while i < n and command[i] in "&|":
1048
+ i += 1
1049
+ else:
1050
+ buf.append(c)
1051
+ has_unquoted = True
1052
+ has_any = True
1053
+ i += 1
1054
+ end_sub()
1055
+ return subs
1056
+
1057
+
1058
+ def masked_subcommands(command: str) -> List[str]:
1059
+ """Return per-subcommand strings with content-only quoted spans masked.
1060
+
1061
+ Used for command-prefix (``anchored``) rules so quoted argument text —
1062
+ commit messages, echo strings, heredoc bodies — cannot false-match a
1063
+ command rule, while quoting obfuscation of the command itself still
1064
+ normalizes to the literal form.
1065
+ """
1066
+ command = _strip_heredoc_bodies(command)
1067
+
1068
+ assigns: Dict[str, str] = {}
1069
+ for m in _ASSIGN_RE.finditer(command):
1070
+ assigns[m.group(1)] = m.group(2).strip("'\"")
1071
+
1072
+ def _resolve(tok: str) -> str:
1073
+ for name, val in assigns.items():
1074
+ tok = tok.replace("${%s}" % name, val).replace("$" + name, val)
1075
+ return tok
1076
+
1077
+ results: List[str] = []
1078
+ for toks in _scan_masked_tokens(command):
1079
+ words: List[str] = []
1080
+ prev = ""
1081
+ for text, fully_quoted in toks:
1082
+ resolved = _resolve(text)
1083
+ is_content = fully_quoted and (not resolved or any(ch in " \t\n" for ch in resolved))
1084
+ if not words and _ASSIGN_TOKEN_RE.match(resolved):
1085
+ # Leading VAR=value assignment — its value is data here (the
1086
+ # assigns table already handles later ``$VAR`` expansion).
1087
+ words.append(_MASK)
1088
+ prev = _MASK
1089
+ continue
1090
+ if is_content:
1091
+ # A ``sh -c "…"`` payload is a command, not content — rescan.
1092
+ if (
1093
+ words
1094
+ and prev.startswith("-")
1095
+ and "c" in prev
1096
+ and words[0].rsplit("/", 1)[-1] in _SHELL_NAMES
1097
+ ):
1098
+ results.extend(masked_subcommands(resolved))
1099
+ words.append(_MASK)
1100
+ prev = _MASK
1101
+ else:
1102
+ words.append(resolved)
1103
+ prev = resolved
1104
+ if words and any(w != _MASK for w in words):
1105
+ results.append(" ".join(words))
1106
+ return results
1107
+
1108
+
921
1109
  # ============================================================================
922
1110
  # DECISION LADDERS
923
1111
  # ============================================================================
@@ -1003,9 +1191,12 @@ def check_command(command: str, config: Dict[str, Any]) -> Dict[str, Any]:
1003
1191
  # ``ask`` at the end (the unattended resolver turns that into a block).
1004
1192
  subcommands, ambiguous = normalize_subcommands(command)
1005
1193
  haystacks = [command] + [s for s in subcommands if s and s != command]
1194
+ # Anchored (command-prefix) rules match only masked subcommands, so quoted
1195
+ # argument text — commit messages, echo strings — can't false-match (#675).
1196
+ masked_haystacks = masked_subcommands(command)
1006
1197
 
1007
- def _search(pat: str) -> bool:
1008
- for hay in haystacks:
1198
+ def _search(pat: str, hays: List[str]) -> bool:
1199
+ for hay in hays:
1009
1200
  try:
1010
1201
  if re.search(pat, hay, re.IGNORECASE):
1011
1202
  return True
@@ -1025,8 +1216,9 @@ def check_command(command: str, config: Dict[str, Any]) -> Dict[str, Any]:
1025
1216
  reason = pattern_obj.get("reason", "Matched pattern")
1026
1217
  should_ask = pattern_obj.get("ask", False)
1027
1218
  bypassable = pattern_obj.get("bypassable", False)
1219
+ anchored = pattern_obj.get("anchored", False)
1028
1220
  try:
1029
- if _search(pattern):
1221
+ if _search(pattern, masked_haystacks if anchored else haystacks):
1030
1222
  if should_ask:
1031
1223
  return {
1032
1224
  "decision": "ask",
@@ -117,6 +117,25 @@ READ_ONLY_BLOCKED = (
117
117
 
118
118
  NO_DELETE_BLOCKED = DELETE_PATTERNS
119
119
 
120
+ # Interpreter programs can write a file without any shell-level write token:
121
+ # ``python3 -c 'open(p, "w")...'``, a heredoc piped into ``python -``, or
122
+ # ``echo 'open(p,"w")' | python3``. The program text is opaque to static
123
+ # matching, so for PROTECTED control-plane paths we fail closed: an interpreter
124
+ # invocation that takes inline code (-c/-e, combined forms like ``perl -pie``),
125
+ # a heredoc/stdin program, or has data piped into it, AND mentions a protected
126
+ # path anywhere in the command, is blocked (#678). Applied ONLY by
127
+ # ``check_protected_command`` — never to user readOnlyPaths rules — so a
128
+ # legitimate read via ``cat``/``grep`` stays allowed.
129
+ _INTERPRETERS = r'(?:python[\d.]*|perl|ruby|node|deno|bun|php)'
130
+ INTERPRETER_WRITE_PATTERNS = [
131
+ # interpreter ... -c/-e/... or stdin/heredoc marker, path anywhere after
132
+ (r'\b' + _INTERPRETERS +
133
+ r'\b[^\n|;&]*(?:\s-\S*[ceE]\b|\s+-\s|\s+-$|<<)(?s:.*){path}',
134
+ "interpreter program"),
135
+ # path anywhere before a pipe into an interpreter
136
+ (r'{path}(?s:.*)\|\s*' + _INTERPRETERS + r'\b', "pipe into interpreter"),
137
+ ]
138
+
120
139
 
121
140
  # ============================================================================
122
141
  # GLOB / PATH HELPERS
@@ -412,7 +431,11 @@ def check_path_patterns(
412
431
  Returns ``(matched, reason)`` where reason names the operation that triggered.
413
432
  """
414
433
  if is_glob_pattern(path):
415
- path_regex = glob_to_regex(path)
434
+ # Allow an optional (possibly quoted) directory prefix: ``*`` in the
435
+ # glob can't span ``/``, so without this a basename glob like
436
+ # ``*.agentwire.yml`` never matched an absolute target
437
+ # (``> /repo/.agentwire.yml`` sailed past every write pattern) (#678).
438
+ path_regex = r'["\']?(?:[^\s"\';|&<>]*/)?' + glob_to_regex(path)
416
439
  else:
417
440
  expanded = os.path.expanduser(path)
418
441
  if expanded == path:
@@ -480,6 +503,9 @@ def load_write_patterns_from_tooldefs(tooldefs_dir: Optional[Path]) -> List[Dict
480
503
  "pattern": regex,
481
504
  "reason": f"{tool_name}: {cmd_def.get('description', cmd_str)}",
482
505
  "ask": True,
506
+ # Command-prefix rule: match command position only, never
507
+ # quoted argument/message content (#675).
508
+ "anchored": True,
483
509
  }
484
510
  # An explicit ``id:`` on the tooldef command yields a stable
485
511
  # rule ID (e.g. ``git.push``) that survives description edits —
@@ -802,7 +828,10 @@ def check_protected_command(
802
828
  for path in _protected_path_patterns():
803
829
  for hay in haystacks:
804
830
  matched, reason = check_path_patterns(
805
- hay, path, READ_ONLY_BLOCKED, "protected control-plane path"
831
+ hay,
832
+ path,
833
+ READ_ONLY_BLOCKED + INTERPRETER_WRITE_PATTERNS,
834
+ "protected control-plane path",
806
835
  )
807
836
  if matched:
808
837
  op = _infer_operation_from_reason(reason)
@@ -914,6 +943,165 @@ def normalize_subcommands(command: str) -> Tuple[List[str], Optional[str]]:
914
943
  return subs, None
915
944
 
916
945
 
946
+ #
947
+ # CONTENT MASKING (#675)
948
+ #
949
+ # Tooldef rules are COMMAND-PREFIX rules (``\bgh\s+pr\s+comment\b``), but the
950
+ # haystacks above include quoted argument text — so a commit message that
951
+ # merely *mentions* "gh pr comment" false-matches the rule. Anchored rules are
952
+ # therefore matched against MASKED subcommands instead: quoted spans that can
953
+ # only be content (a quoted token containing whitespace — a message, an echo
954
+ # string, a heredoc body) are replaced with a placeholder, while quoted
955
+ # single words are kept (``"gh" pr comment`` is still a real command) and
956
+ # ``sh -c "…"`` payloads are recursively re-scanned as commands.
957
+
958
+ _MASK = "\x00"
959
+ _HEREDOC_RE = re.compile(r"<<-?\s*(['\"]?)([A-Za-z_]\w+)\1")
960
+ _SHELL_NAMES = {"sh", "bash", "zsh", "dash", "ksh"}
961
+ _ASSIGN_TOKEN_RE = re.compile(r"^[A-Za-z_]\w*=")
962
+
963
+
964
+ def _strip_heredoc_bodies(command: str) -> str:
965
+ """Remove heredoc body lines (``<<EOF … EOF``) — they are content."""
966
+ m = _HEREDOC_RE.search(command)
967
+ if not m:
968
+ return command
969
+ delim = m.group(2)
970
+ nl = command.find("\n", m.end())
971
+ if nl == -1:
972
+ return command
973
+ lines = command[nl + 1:].split("\n")
974
+ for i, line in enumerate(lines):
975
+ if line.strip() == delim:
976
+ rest = "\n".join(lines[i + 1:])
977
+ return command[:nl + 1] + _strip_heredoc_bodies(rest)
978
+ # Unterminated heredoc: everything after the marker is body.
979
+ return command[:nl + 1]
980
+
981
+
982
+ def _scan_masked_tokens(command: str) -> List[List[Tuple[str, bool]]]:
983
+ """Split ``command`` into subcommands of ``(token_text, fully_quoted)``.
984
+
985
+ Quotes/escapes are resolved into the token text (so ``g''h`` -> ``gh``);
986
+ ``fully_quoted`` is True when no character of the token was unquoted.
987
+ """
988
+ subs: List[List[Tuple[str, bool]]] = []
989
+ cur: List[Tuple[str, bool]] = []
990
+ buf: List[str] = []
991
+ has_unquoted = False
992
+ has_any = False
993
+
994
+ def end_token() -> None:
995
+ nonlocal buf, has_unquoted, has_any
996
+ if has_any:
997
+ cur.append(("".join(buf), not has_unquoted))
998
+ buf, has_unquoted, has_any = [], False, False
999
+
1000
+ def end_sub() -> None:
1001
+ nonlocal cur
1002
+ end_token()
1003
+ if cur:
1004
+ subs.append(cur)
1005
+ cur = []
1006
+
1007
+ i, n = 0, len(command)
1008
+ while i < n:
1009
+ c = command[i]
1010
+ if c == "'":
1011
+ j = command.find("'", i + 1)
1012
+ if j == -1:
1013
+ j = n
1014
+ buf.append(command[i + 1:j])
1015
+ has_any = True
1016
+ i = j + 1
1017
+ elif c == '"':
1018
+ j = i + 1
1019
+ out: List[str] = []
1020
+ while j < n and command[j] != '"':
1021
+ if command[j] == "\\" and j + 1 < n and command[j + 1] in '"\\$`':
1022
+ out.append(command[j + 1])
1023
+ j += 2
1024
+ else:
1025
+ out.append(command[j])
1026
+ j += 1
1027
+ buf.append("".join(out))
1028
+ has_any = True
1029
+ i = j + 1
1030
+ elif c == "\\" and i + 1 < n:
1031
+ buf.append(command[i + 1])
1032
+ has_unquoted = True
1033
+ has_any = True
1034
+ i += 2
1035
+ elif c in " \t":
1036
+ end_token()
1037
+ i += 1
1038
+ elif c in ";\n":
1039
+ end_sub()
1040
+ i += 1
1041
+ elif c in "&|":
1042
+ end_sub()
1043
+ while i < n and command[i] in "&|":
1044
+ i += 1
1045
+ else:
1046
+ buf.append(c)
1047
+ has_unquoted = True
1048
+ has_any = True
1049
+ i += 1
1050
+ end_sub()
1051
+ return subs
1052
+
1053
+
1054
+ def masked_subcommands(command: str) -> List[str]:
1055
+ """Return per-subcommand strings with content-only quoted spans masked.
1056
+
1057
+ Used for command-prefix (``anchored``) rules so quoted argument text —
1058
+ commit messages, echo strings, heredoc bodies — cannot false-match a
1059
+ command rule, while quoting obfuscation of the command itself still
1060
+ normalizes to the literal form.
1061
+ """
1062
+ command = _strip_heredoc_bodies(command)
1063
+
1064
+ assigns: Dict[str, str] = {}
1065
+ for m in _ASSIGN_RE.finditer(command):
1066
+ assigns[m.group(1)] = m.group(2).strip("'\"")
1067
+
1068
+ def _resolve(tok: str) -> str:
1069
+ for name, val in assigns.items():
1070
+ tok = tok.replace("${%s}" % name, val).replace("$" + name, val)
1071
+ return tok
1072
+
1073
+ results: List[str] = []
1074
+ for toks in _scan_masked_tokens(command):
1075
+ words: List[str] = []
1076
+ prev = ""
1077
+ for text, fully_quoted in toks:
1078
+ resolved = _resolve(text)
1079
+ is_content = fully_quoted and (not resolved or any(ch in " \t\n" for ch in resolved))
1080
+ if not words and _ASSIGN_TOKEN_RE.match(resolved):
1081
+ # Leading VAR=value assignment — its value is data here (the
1082
+ # assigns table already handles later ``$VAR`` expansion).
1083
+ words.append(_MASK)
1084
+ prev = _MASK
1085
+ continue
1086
+ if is_content:
1087
+ # A ``sh -c "…"`` payload is a command, not content — rescan.
1088
+ if (
1089
+ words
1090
+ and prev.startswith("-")
1091
+ and "c" in prev
1092
+ and words[0].rsplit("/", 1)[-1] in _SHELL_NAMES
1093
+ ):
1094
+ results.extend(masked_subcommands(resolved))
1095
+ words.append(_MASK)
1096
+ prev = _MASK
1097
+ else:
1098
+ words.append(resolved)
1099
+ prev = resolved
1100
+ if words and any(w != _MASK for w in words):
1101
+ results.append(" ".join(words))
1102
+ return results
1103
+
1104
+
917
1105
  # ============================================================================
918
1106
  # DECISION LADDERS
919
1107
  # ============================================================================
@@ -999,9 +1187,12 @@ def check_command(command: str, config: Dict[str, Any]) -> Dict[str, Any]:
999
1187
  # ``ask`` at the end (the unattended resolver turns that into a block).
1000
1188
  subcommands, ambiguous = normalize_subcommands(command)
1001
1189
  haystacks = [command] + [s for s in subcommands if s and s != command]
1190
+ # Anchored (command-prefix) rules match only masked subcommands, so quoted
1191
+ # argument text — commit messages, echo strings — can't false-match (#675).
1192
+ masked_haystacks = masked_subcommands(command)
1002
1193
 
1003
- def _search(pat: str) -> bool:
1004
- for hay in haystacks:
1194
+ def _search(pat: str, hays: List[str]) -> bool:
1195
+ for hay in hays:
1005
1196
  try:
1006
1197
  if re.search(pat, hay, re.IGNORECASE):
1007
1198
  return True
@@ -1021,8 +1212,9 @@ def check_command(command: str, config: Dict[str, Any]) -> Dict[str, Any]:
1021
1212
  reason = pattern_obj.get("reason", "Matched pattern")
1022
1213
  should_ask = pattern_obj.get("ask", False)
1023
1214
  bypassable = pattern_obj.get("bypassable", False)
1215
+ anchored = pattern_obj.get("anchored", False)
1024
1216
  try:
1025
- if _search(pattern):
1217
+ if _search(pattern, masked_haystacks if anchored else haystacks):
1026
1218
  if should_ask:
1027
1219
  return {
1028
1220
  "decision": "ask",