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