agentwire-dev 1.65.2__tar.gz → 1.65.3__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.65.2 → agentwire_dev-1.65.3}/PKG-INFO +1 -1
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/__init__.py +1 -1
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/mcp_core.py +23 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/mcp_session.py +5 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/send_cli.py +87 -5
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/server.py +14 -3
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/session_ready.py +15 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_cli_commands.py +161 -8
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_mcp_server.py +51 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_mcp_tools_args.py +4 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_portal_api.py +64 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_session_ready.py +22 -1
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/.agentwire.yml.example +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/.claude/commands/handoff.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/.claude/skills/wiki/SKILL.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/.github/FUNDING.yml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/.github/ISSUE_TEMPLATE/question.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/.github/workflows/pytest.yml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/.github/workflows/ruff.yml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/.github/workflows/security.yml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/.github/workflows/tts-smoke.yml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/.gitignore +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/AGENTS.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/CODE_OF_CONDUCT.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/CONTRIBUTING.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/Dockerfile.local +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/LICENSE +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/README.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/RELEASING.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/SECURITY.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/SPONSORS.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/__main__.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/agents/__init__.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/agents/base.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/agents/tmux.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/cached_status.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/channels/__init__.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/channels/base.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/channels/email.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/channels/push.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/channels/quo.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/channels_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/chrome_tabs.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/completion.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/config.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/config_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/core.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/council/__init__.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/council/cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/council/inbox.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/council/minutes.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/council/state.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/council/view.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/devices.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/diff_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/doctor_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/doctor_voice.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/ensure_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/fetch.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/handoff/__init__.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/handoff/git_state.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/handoff/instructions.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/handoff/parser.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/handoff/renderer.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/handoff/schema.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/handoff_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/history.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/history_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/hooks/__init__.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/hooks/agentwire-permission.sh +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/hooks/damage-control/__init__.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/hooks/damage-control/audit_logger.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/hooks/damage-control/bash-tool-damage-control.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/hooks/damage-control/edit-tool-damage-control.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/hooks/damage-control/mcp-tool-damage-control.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/hooks/damage-control/read-tool-damage-control.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/hooks/damage-control/rules/agentwire.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/hooks/damage-control/rules/aws.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/hooks/damage-control/rules/cloud-hosting.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/hooks/damage-control/rules/containers.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/hooks/damage-control/rules/control-plane.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/hooks/damage-control/rules/core.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/hooks/damage-control/rules/databases.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/hooks/damage-control/rules/firebase.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/hooks/damage-control/rules/gcp.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/hooks/damage-control/rules/git.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/hooks/damage-control/rules/gws.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/hooks/damage-control/rules/infrastructure.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/hooks/damage-control/rules/outbound.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/hooks/damage-control/rules/publish.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/hooks/damage-control/rules/remote.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/hooks/damage-control/write-tool-damage-control.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/hooks/idle-handler.sh +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/hooks/queue-processor.sh +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/hooks_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/inbox.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/limits_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/listen.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/locking.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/machine_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/mcp_channels.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/mcp_chrome.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/mcp_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/mcp_council.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/mcp_desktop.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/mcp_handoff.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/mcp_history.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/mcp_listen.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/mcp_lock.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/mcp_machine.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/mcp_msg.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/mcp_notify.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/mcp_pane.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/mcp_projects.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/mcp_scheduler.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/mcp_scratchpad.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/mcp_server.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/mcp_services.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/mcp_status.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/mcp_task.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/mcp_tunnels.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/mcp_voice.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/mcp_wiki.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/mcp_worktree.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/msg_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/network.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/notify_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/onboarding.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/palette_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/pane_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/pane_manager.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/portal_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/project_config.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/projects.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/prompt_router.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/prompts_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/push_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/push_store.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/repo_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/research.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/roles/__init__.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/roles/agentwire.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/roles/anchor.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/roles/chatbot.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/roles/contributor.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/roles/correspondent.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/roles/council-brain.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/roles/council-conscience.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/roles/council-critic.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/roles/council-devils-advocate.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/roles/council-gut.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/roles/council-historian.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/roles/council-member.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/roles/council-orchestrator.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/roles/init.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/roles/notifications.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/roles/orchestrator.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/roles/reviewer-worktree.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/roles/reviewer.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/roles/soul.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/roles/task-runner.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/roles/voice.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/roles/worker-worktree.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/roles/worker.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/roles_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/routes/__init__.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/routes/artifacts.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/routes/config.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/routes/council.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/routes/desktop.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/routes/history.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/routes/machines.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/routes/notify.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/routes/palette.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/routes/permission.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/routes/projects.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/routes/push.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/routes/safety.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/routes/scheduler.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/routes/scratchpad.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/routes/sessions.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/routes/sessions_admin.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/routes/voice.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/safety/__init__.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/safety/_core.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/safety_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/safety_commands.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/scheduler/__init__.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/scheduler/dispatch.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/scheduler/gates.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/scheduler/loop.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/scheduler/models.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/scheduler/report.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/scheduler/schedule.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/scheduler/state.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/scheduler/zombie.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/scheduler_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/scratchpad.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/security.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/services.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/session_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/session_context.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/ssh.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/agentwire-splash-logo-layers--agentwire-text.png +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/agentwire-splash-logo-layers--echo-claw-fg.png +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/agentwire-splash-logo-layers--echo.png +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/agentwire-splash-logo-layers--telephone.png +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/agentwire-splash-logo-layers--tree.png +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/announcements.json +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/apple-touch-icon.png +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/css/desktop.css +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/css/mobile.css +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/favicon-32.png +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/favicon-64.png +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/icons/sessions/bear.webp +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/icons/sessions/cat.webp +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/icons/sessions/crown.webp +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/icons/sessions/custom/agentwire-portal.webp +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/icons/sessions/custom/agentwire-tts.webp +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/icons/sessions/custom/agentwire.webp +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/icons/sessions/deer.webp +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/icons/sessions/drone.webp +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/icons/sessions/eagle.webp +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/icons/sessions/fox.webp +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/icons/sessions/hawk.webp +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/icons/sessions/horse.webp +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/icons/sessions/lion.webp +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/icons/sessions/rabbit.webp +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/icons/sessions/robot.webp +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/icons/sessions/tiger.webp +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/icons/sessions/wolf.webp +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/img/icon-192.png +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/img/icon-512.png +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/.gitkeep +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/announcement-modal.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/api.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/artifact-window.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/card-terminal.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/collage.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/command-palette.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/council-window.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/dead-key-suppressor.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/desktop-manager.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/desktop.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/help-modal.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/icon-manager.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/lineage.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/mobile.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/notification-prefs.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/notifications-panel.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/ptt.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/push-client.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/review-window.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/safety-shared.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/safety-window.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/scratchpad.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/service-classification.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/session-hud-controller.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/session-hud-notices.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/session-hud-spawn.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/session-hud.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/session-id.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/session-window.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/shortcuts.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/sidebar/artifacts-section.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/sidebar/config-section.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/sidebar/council-section.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/sidebar/machines-section.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/sidebar/projects-section.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/sidebar/safety-section.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/sidebar/scheduler-section.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/sidebar/services-section.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/sidebar/sessions-section.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/sidebar.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/spawn-ghost.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/terminal-font-prefs.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/terminal-pane.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/tile-manager.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/toast.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/token-modal.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/topology-render.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/utils/ansi.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/utils/rich-text.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/utils/swipe.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/voice/autosend-prefs.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/voice/browser-stt.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/voice/browser-tts.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/voice/jargon.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/voice/prompt.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/winbox.bundle.min.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/js/workspace-window.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/manifest.webmanifest +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/service-worker.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/static/topology-harness.html +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/stt/__init__.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/stt/base.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/stt/cloud.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/stt/engine.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/stt/local.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/stt/server_backend.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/stt/stt_server.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/system_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/tabs_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/tasks.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/tasks_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/templates/__init__.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/templates/base.html +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/templates/council/minutes.html.j2 +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/templates/desktop.html +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/templates/email_notification.html +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/templates/handoff/show-the-story.html.j2 +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/templates/handoff/theme.css.j2 +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/templates/mobile.html +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/templates/pair.html +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/templates/tmux.conf +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/templating.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/tooldefs/aws.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/tooldefs/docker.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/tooldefs/gcp.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/tooldefs/gh.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/tooldefs/git.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/tooldefs/gws.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/tooldefs/kubectl.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/tooldefs/npm.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/tooldefs/terraform.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/tooldefs/uv.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/tts/__init__.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/tts/audio.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/tts/base.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/tts/engines/__init__.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/tts/engines/chatterbox.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/tts/engines/kokoro.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/tts/engines/zonos.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/tts/kokoro_server.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/tts/local.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/tts/registry.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/tts_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/tts_server.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/tunnels.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/tunnels_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/usage_limit.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/utils/__init__.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/utils/chunker.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/utils/event_log.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/utils/file_io.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/utils/paths.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/utils/speech.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/utils/subprocess.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/validation.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/voice_status.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/wiki.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/wiki_audit.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/wiki_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/worktree.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/agentwire/worktree_registry.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/decisions/obsidian-second-brain.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/design/session-card-fields.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/design/session-card-recommendation.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/logo.png +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/INDEX.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/architecture.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/briefing-mode.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/communication/channels.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/communication/hammerspoon.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/communication/handoff.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/concepts.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/council.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/deployment/remote-access.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/deployment/remote-machines.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/glossary.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/integrations/gws-google-workspace-cli.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/internals/damage-control.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/internals/parallel-refactor.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/internals/portal.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/internals/session-topology.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/internals/shell-escaping.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/internals/troubleshooting.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/internals/window-collage.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/quickstart.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/research/briefing-mode-feasibility.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/research/orchestration-transport-alternatives.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/scheduling/scheduled-workloads.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/security/damage-control-hardening.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/security/pip-audit.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/security/remote-access-hardening.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/security/secrets.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/services.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/sessions/claude-code-auto-mode.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/sessions/messaging.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/sessions/prompt-routing.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/sessions/window-sizing.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/sessions/worktree-sessions.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/usage-limit-recovery.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/voice/shim-contract.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/voice/stt-cloud.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/voice/stt-self-hosted.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/docs/wiki/voice/tts-self-hosted.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/pyproject.toml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/conftest.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/e2e/test_portal_ui.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/fixtures/sample_agentwire.yml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/fixtures/sample_config.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/fixtures/sample_scheduler.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/integration/test_council_portal.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/integration/test_scheduler_board.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/integration/test_send_verified_tmux.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/integration/test_server_websockets.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/integration/test_worktree_cmd.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_announcements.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_build_agent_command.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_channels.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_chrome_tabs.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_cli_output.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_cli_smoke.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_config.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_config_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_control_plane_protection.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_core_created_by_rooting.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_council_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_council_inbox.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_council_minutes.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_council_state.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_council_view.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_damage_control_bypass.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_damage_control_hooks.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_damage_control_sync.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_dangling_worktrees.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_devices.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_diff_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_doctor_scheduler_staleness.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_doctor_task_migration.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_doctor_voice.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_event_log.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_file_io.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_guarded_launch_command.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_handoff_git_state.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_handoff_instructions.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_handoff_parser.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_handoff_renderer.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_history.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_hooks_install.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_idle_handler.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_idle_nag.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_inbox.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_init_command.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_install_parity.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_limits_tick_isolation.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_locking.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_msg_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_notify_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_onboarding_reinit.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_palette_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_pip_environment.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_posture_default.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_project_config.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_projects_registry.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_prompt_router.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_push.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_rebuild_guard.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_repo_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_rich_text_js.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_roles.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_routes_snapshot.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_safety_commands.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_safety_disabled_rules.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_safety_escape_hatch.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_safety_heal.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_safety_kill_switch.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_scheduler.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_scheduler_parsing.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_scheduler_zombie.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_scratchpad.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_security.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_server_async.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_server_pure.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_services.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_session_context.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_session_created_by_env.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_shared_dir_guard.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_shim_selfheal.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_skill_install.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_speech_tags.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_ssh.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_stt_backend.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_stt_cloud.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_stt_engine.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_task_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_tasks.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_tasks_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_templating.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_terminal_resize.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_tmux_template.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_tts_engine_resolution.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_tts_local.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_tunnels_probe.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_unattended_env_isolation.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_usage_limit.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_validation.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_voice_status.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_wiki.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_wiki_audit.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_window_cycling_binding.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_worktree.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_worktree_branch_cleanup.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_worktree_layout.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.65.3}/tests/unit/test_worktree_registry.py +0 -0
|
@@ -162,11 +162,34 @@ def _delivery_result(data: dict, where: str) -> str:
|
|
|
162
162
|
returns ``verified``: True (landed), False (sent but not seen — likely a
|
|
163
163
|
busy/booting pane that dropped it), or None (remote — unverifiable across
|
|
164
164
|
SSH). Surface that instead of a blind "sent".
|
|
165
|
+
|
|
166
|
+
A ``False`` verify from a *session*-level send no longer just hands the
|
|
167
|
+
problem back to whichever caller reads this string (#834) — the CLI
|
|
168
|
+
falls back to the durable msg inbox (retried across ticks, dead-lettered
|
|
169
|
+
+ emailed on true exhaustion) when the direct paste can't be confirmed,
|
|
170
|
+
so report THAT outcome rather than telling the caller to notice and
|
|
171
|
+
resend by hand. A worker *pane* send has no such fallback (the msg inbox
|
|
172
|
+
only addresses sessions, not individual panes) — ``data`` won't carry a
|
|
173
|
+
``fallback`` key at all in that case, distinguishing "not attempted"
|
|
174
|
+
from "attempted and failed" so this never claims a fallback that never
|
|
175
|
+
ran.
|
|
165
176
|
"""
|
|
166
177
|
verified = data.get("verified")
|
|
167
178
|
if verified is True:
|
|
168
179
|
return f"Message delivered {where} (verified in pane)."
|
|
169
180
|
if verified is False:
|
|
181
|
+
if "fallback" in data:
|
|
182
|
+
if data.get("fallback") == "already_delivered":
|
|
183
|
+
return (f"Message sent {where} — the pane confirmation was ambiguous, but the "
|
|
184
|
+
f"message is already visible on scrollback, so it was in fact delivered. "
|
|
185
|
+
f"No action needed.")
|
|
186
|
+
if data.get("fallback") == "inbox":
|
|
187
|
+
return (f"Message sent {where} but delivery could NOT be verified in the pane — "
|
|
188
|
+
f"queued to its msg inbox instead, which guarantees delivery (retried "
|
|
189
|
+
f"automatically, dead-lettered + emailed to the owner only if it truly "
|
|
190
|
+
f"can't land). No action needed.")
|
|
191
|
+
return (f"Message sent {where} but delivery could NOT be verified, AND the msg-inbox "
|
|
192
|
+
f"fallback also failed — this message may be lost. Check the pane or resend.")
|
|
170
193
|
return (f"Message sent {where} but delivery could NOT be verified — it may "
|
|
171
194
|
f"have been dropped (busy/booting pane). Check the pane or resend.")
|
|
172
195
|
if verified is None and "verified" in data:
|
|
@@ -227,6 +227,11 @@ def session_send(session: str, message: str) -> str:
|
|
|
227
227
|
f"{message}"
|
|
228
228
|
)
|
|
229
229
|
args = ["send", "-s", session, "--verify", message]
|
|
230
|
+
if caller:
|
|
231
|
+
# Attributes a msg-inbox delivery fallback to the real caller instead
|
|
232
|
+
# of the generic "agentwire" (#835 review) — the CLI subprocess can't
|
|
233
|
+
# auto-detect the caller across the MCP boundary itself.
|
|
234
|
+
args.extend(["--caller-session", caller])
|
|
230
235
|
data = run_agentwire_cmd(args)
|
|
231
236
|
if not data.get("success"):
|
|
232
237
|
return f"Failed to send message: {data.get('error', 'Unknown error')}"
|
|
@@ -24,6 +24,60 @@ from .core import (
|
|
|
24
24
|
)
|
|
25
25
|
|
|
26
26
|
|
|
27
|
+
def _recover_unverified_send(session: str, prompt: str, sender: str) -> "str | None":
|
|
28
|
+
"""Fallback when `send_verified` can't confirm delivery (#834, #835 review).
|
|
29
|
+
|
|
30
|
+
First checks whether the bare message is already on scrollback outside
|
|
31
|
+
the input box. A `send_verified` failure can mean the paste genuinely
|
|
32
|
+
never landed/submitted, OR that it fully submitted and only the
|
|
33
|
+
*confirm* read was ambiguous (an unparseable box frame, or a laggy host
|
|
34
|
+
blowing the submit budget). The msg-inbox drain's own dedup can't catch
|
|
35
|
+
the latter case on its own: it matches the WRAPPED ``[MSG from ... ]
|
|
36
|
+
... <id>`` render (see ``inbox.Message.render``), never the bare text
|
|
37
|
+
this path pasted directly — enqueuing unconditionally would risk a real
|
|
38
|
+
duplicate delivery rather than the safe no-op the drain's dedup gives
|
|
39
|
+
its own messages. Checking here first avoids that duplicate in the
|
|
40
|
+
common case.
|
|
41
|
+
|
|
42
|
+
A message that lands only *after* this check runs (queued invisibly
|
|
43
|
+
mid-generation, per ``submit_confirmed``'s own docstring) can still
|
|
44
|
+
duplicate — accepted per this codebase's existing bias that a
|
|
45
|
+
recoverable duplicate beats a silently dropped message.
|
|
46
|
+
|
|
47
|
+
Known residual risk (#835 second-pass review): this is the same bare
|
|
48
|
+
whitespace-normalized substring match ``_deliver_once`` already uses for
|
|
49
|
+
its own internal idempotent-paste guard — not a new invention — but
|
|
50
|
+
reused here it trades in the *opposite* direction from most of this
|
|
51
|
+
file's checks. A false match reports "already delivered" and SKIPS the
|
|
52
|
+
inbox enqueue entirely: for a short or generic message (a bare "yes",
|
|
53
|
+
"continue", "approved") that happens to already sit in the last
|
|
54
|
+
``VERIFY_SCROLLBACK_LINES`` for an unrelated reason, a send that
|
|
55
|
+
genuinely never landed could be silently dropped instead of queued —
|
|
56
|
+
the one outcome this whole fallback exists to prevent. Tracked as a
|
|
57
|
+
follow-up (a lightweight per-attempt marker, mirroring
|
|
58
|
+
``send_verified``'s own ``marker`` param, would close this precisely);
|
|
59
|
+
not fixed here because it's strictly narrower than the bug this
|
|
60
|
+
function was written to close (needs a coincidental/repeated-text
|
|
61
|
+
match, not just any unverified send).
|
|
62
|
+
|
|
63
|
+
Returns ``"already_delivered"``, ``"inbox"``, or ``None`` (the inbox
|
|
64
|
+
fallback itself failed).
|
|
65
|
+
"""
|
|
66
|
+
from agentwire.session_ready import message_on_scrollback, recover_failed_seed, scrollback
|
|
67
|
+
|
|
68
|
+
if message_on_scrollback(scrollback(session), prompt):
|
|
69
|
+
return "already_delivered"
|
|
70
|
+
return recover_failed_seed(session, prompt, sender=sender)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _fallback_suffix(fallback: "str | None") -> str:
|
|
74
|
+
if fallback == "already_delivered":
|
|
75
|
+
return " — already delivered directly (the confirmation read was just ambiguous); no action needed"
|
|
76
|
+
if fallback == "inbox":
|
|
77
|
+
return " — queued to its msg inbox for guaranteed delivery"
|
|
78
|
+
return " and could not be queued — resend manually"
|
|
79
|
+
|
|
80
|
+
|
|
27
81
|
def cmd_send(args) -> int:
|
|
28
82
|
"""Send a prompt to a tmux session or pane (adds Enter automatically).
|
|
29
83
|
|
|
@@ -36,6 +90,13 @@ def cmd_send(args) -> int:
|
|
|
36
90
|
json_mode = getattr(args, 'json', False)
|
|
37
91
|
wait_ready = getattr(args, 'wait_ready', False)
|
|
38
92
|
verify = getattr(args, 'verify', False)
|
|
93
|
+
# Candidate sender for a msg-inbox fallback's attribution (#835 review):
|
|
94
|
+
# prefer the real calling session over the generic "agentwire" so
|
|
95
|
+
# dead-letter emails and the rendered [MSG from ...] header stay
|
|
96
|
+
# meaningful. MCP forwards --caller-session explicitly (can't reliably
|
|
97
|
+
# auto-detect the caller across that boundary); a bare CLI invocation
|
|
98
|
+
# falls back to auto-detecting its own tmux session.
|
|
99
|
+
fallback_sender = getattr(args, 'caller_session', None) or pane_manager.get_current_session() or "agentwire"
|
|
39
100
|
|
|
40
101
|
if wait_ready and pane_index is not None:
|
|
41
102
|
return _output_result(False, json_mode, "--wait-ready targets a session's pane 0; it can't be combined with --pane")
|
|
@@ -173,13 +234,22 @@ def cmd_send(args) -> int:
|
|
|
173
234
|
if not wait_for_session_ready(session, timeout=timeout):
|
|
174
235
|
return _output_result(False, json_mode, f"Agent in '{session}' not ready after {timeout:.0f}s")
|
|
175
236
|
if not send_verified(session, prompt):
|
|
237
|
+
# A caller acting on "not verified" text is optional, not
|
|
238
|
+
# guaranteed — under load this is exactly the class of message
|
|
239
|
+
# that must never depend on an LLM noticing and manually
|
|
240
|
+
# resending. Fall back to the durable msg inbox (retried across
|
|
241
|
+
# ticks, dead-lettered + emailed on true exhaustion) instead of
|
|
242
|
+
# returning inert advisory text (#834).
|
|
243
|
+
fallback = _recover_unverified_send(session, prompt, fallback_sender)
|
|
176
244
|
if json_mode:
|
|
177
|
-
print(json.dumps({"success": False, "session": session_full, "verified": False,
|
|
245
|
+
print(json.dumps({"success": False, "session": session_full, "verified": False,
|
|
246
|
+
"fallback": fallback, "error": "Delivery not verified"}))
|
|
178
247
|
else:
|
|
179
|
-
print(f"Sent to {session} but delivery could not be verified", file=sys.stderr)
|
|
248
|
+
print(f"Sent to {session} but delivery could not be verified{_fallback_suffix(fallback)}", file=sys.stderr)
|
|
180
249
|
return 1
|
|
181
250
|
if json_mode:
|
|
182
|
-
print(json.dumps({"success": True, "session": session_full, "machine": None,
|
|
251
|
+
print(json.dumps({"success": True, "session": session_full, "machine": None,
|
|
252
|
+
"verified": True, "fallback": None, "message": "Prompt sent"}))
|
|
183
253
|
else:
|
|
184
254
|
print(f"Sent to {session} (verified)")
|
|
185
255
|
return 0
|
|
@@ -197,12 +267,19 @@ def cmd_send(args) -> int:
|
|
|
197
267
|
from agentwire.session_ready import send_verified
|
|
198
268
|
|
|
199
269
|
ok = send_verified(session, prompt, retries=1)
|
|
270
|
+
fallback = None
|
|
271
|
+
if not ok:
|
|
272
|
+
# Same durable fallback as --wait-ready (#834).
|
|
273
|
+
fallback = _recover_unverified_send(session, prompt, fallback_sender)
|
|
200
274
|
if json_mode:
|
|
201
275
|
print(json.dumps({"success": True, "session": session_full, "machine": None,
|
|
202
|
-
"verified": ok,
|
|
276
|
+
"verified": ok, "fallback": fallback,
|
|
203
277
|
"message": "Prompt sent" if ok else "Prompt sent but delivery could not be verified"}))
|
|
204
278
|
else:
|
|
205
|
-
|
|
279
|
+
if ok:
|
|
280
|
+
print(f"Sent to {session} (verified)")
|
|
281
|
+
else:
|
|
282
|
+
print(f"Sent to {session} (delivery NOT verified){_fallback_suffix(fallback)}")
|
|
206
283
|
return 0
|
|
207
284
|
|
|
208
285
|
# Delegate paste + Enter handling to the shared pane_manager helper so
|
|
@@ -313,6 +390,11 @@ def register_send_parser(subparsers) -> None:
|
|
|
313
390
|
"report verified vs unconfirmed instead of blind success")
|
|
314
391
|
send_parser.add_argument("--timeout", type=float, default=30.0,
|
|
315
392
|
help="Readiness wait timeout in seconds (with --wait-ready, default: 30)")
|
|
393
|
+
send_parser.add_argument("--caller-session", dest="caller_session",
|
|
394
|
+
help="Internal: attribute a msg-inbox delivery fallback to this sender "
|
|
395
|
+
"instead of auto-detecting the calling tmux session. MCP forwards "
|
|
396
|
+
"this explicitly, since the CLI subprocess can't reliably "
|
|
397
|
+
"auto-detect the caller across that boundary.")
|
|
316
398
|
send_parser.add_argument("--json", action="store_true", help="Output as JSON")
|
|
317
399
|
send_parser.set_defaults(func=cmd_send)
|
|
318
400
|
|
|
@@ -33,7 +33,7 @@ import jinja2
|
|
|
33
33
|
import yaml
|
|
34
34
|
from aiohttp import web
|
|
35
35
|
|
|
36
|
-
from . import prompt_router, security
|
|
36
|
+
from . import __version__, prompt_router, security
|
|
37
37
|
from .cached_status import CachedStatusChecker
|
|
38
38
|
from .config import Config, load_config
|
|
39
39
|
from .routes.artifacts import ArtifactsRoutesMixin, register_artifacts_routes
|
|
@@ -66,8 +66,6 @@ from .security import (
|
|
|
66
66
|
from .ssh import ssh_base_opts
|
|
67
67
|
from .worktree import parse_session_name
|
|
68
68
|
|
|
69
|
-
__version__ = "1.3.0"
|
|
70
|
-
|
|
71
69
|
logger = logging.getLogger(__name__)
|
|
72
70
|
|
|
73
71
|
# Static asset serving (#488): gzip text on the fly + Cache-Control headers.
|
|
@@ -2479,6 +2477,19 @@ class AgentWireServer(
|
|
|
2479
2477
|
["send", "-s", session_name, "--wait-ready", "--timeout", "60", "--", message]
|
|
2480
2478
|
)
|
|
2481
2479
|
if not success:
|
|
2480
|
+
if result.get("fallback") in ("inbox", "already_delivered"):
|
|
2481
|
+
# #835: an unverified send now recovers on its own -- either
|
|
2482
|
+
# it was already delivered and the confirm read was just
|
|
2483
|
+
# ambiguous, or it's queued to the durable msg inbox. Either
|
|
2484
|
+
# way the old "paste it manually" toast would be stale,
|
|
2485
|
+
# actively wrong advice.
|
|
2486
|
+
logger.info(f"First message for {session_name} recovered via {result.get('fallback')} fallback")
|
|
2487
|
+
await self._post_toast(
|
|
2488
|
+
f"First message to {session_name} queued for guaranteed delivery"
|
|
2489
|
+
if result.get("fallback") == "inbox" else
|
|
2490
|
+
f"First message to {session_name} delivered (confirmation was just delayed)",
|
|
2491
|
+
session=session_name, priority="normal", id_prefix="firstmsg")
|
|
2492
|
+
return
|
|
2482
2493
|
logger.warning(f"First message delivery failed for {session_name}: {result.get('error')}")
|
|
2483
2494
|
await self._post_toast(
|
|
2484
2495
|
f"First message not delivered to {session_name} — paste it manually",
|
|
@@ -590,6 +590,19 @@ def clear_input_box(session: str, pane_index: int = 0) -> bool:
|
|
|
590
590
|
text]`` chip) and confirms emptiness with the drain's own SGR-aware gate
|
|
591
591
|
(``prompt_router.prompt_is_empty``), so "cleared" here means exactly
|
|
592
592
|
"the drain will deliver". Returns True iff the box ends up empty.
|
|
593
|
+
|
|
594
|
+
Refuses to press Escape while the screen shows a live select-menu/dialog
|
|
595
|
+
(#835 review): this function was written for a brand-new session's
|
|
596
|
+
failed first-message seed, where nothing else could plausibly be on
|
|
597
|
+
screen. It is also reused for an already-running, independently-busy
|
|
598
|
+
session (``agentwire send --verify`` falling back after a failed
|
|
599
|
+
delivery) — there, "box not empty" can mean a permission dialog or
|
|
600
|
+
``AskUserQuestion`` menu belonging to the recipient's own unrelated
|
|
601
|
+
work, not our stuck paste. Escape is the conventional cancel/decline key
|
|
602
|
+
for those, so pressing it blind would silently cancel a decision that
|
|
603
|
+
had nothing to do with us. Returns False without touching the pane in
|
|
604
|
+
that case; the caller's msg-inbox fallback still gets enqueued and
|
|
605
|
+
waits for the box to become genuinely idle on its own.
|
|
593
606
|
"""
|
|
594
607
|
from agentwire import pane_manager, prompt_router
|
|
595
608
|
|
|
@@ -602,6 +615,8 @@ def clear_input_box(session: str, pane_index: int = 0) -> bool:
|
|
|
602
615
|
try:
|
|
603
616
|
if empty():
|
|
604
617
|
return True
|
|
618
|
+
if prompt_router.screen_shows_live_menu(_snapshot(session, pane_index)):
|
|
619
|
+
return False
|
|
605
620
|
pane_manager.run_command(
|
|
606
621
|
["tmux", "send-keys", "-t", target, "Escape"], timeout=5
|
|
607
622
|
)
|
|
@@ -188,12 +188,19 @@ class TestCmdSendWaitReady:
|
|
|
188
188
|
def _payload(self, capsys):
|
|
189
189
|
return json.loads(capsys.readouterr().out.strip())
|
|
190
190
|
|
|
191
|
+
def _mock_has_session(self, monkeypatch):
|
|
192
|
+
has_session = MagicMock(returncode=0)
|
|
193
|
+
monkeypatch.setattr("agentwire.send_cli.subprocess.run", lambda *a, **k: has_session)
|
|
194
|
+
# get_current_session shells out to real tmux and depends on the test
|
|
195
|
+
# runner's own environment ($TMUX_PANE) -- pin it so fallback_sender
|
|
196
|
+
# resolution is deterministic regardless of where tests run.
|
|
197
|
+
monkeypatch.setattr("agentwire.send_cli.pane_manager.get_current_session", lambda: None)
|
|
198
|
+
|
|
191
199
|
def test_happy_path_verified(self, capsys, monkeypatch):
|
|
192
200
|
from agentwire import session_ready
|
|
193
201
|
from agentwire.send_cli import cmd_send
|
|
194
202
|
|
|
195
|
-
|
|
196
|
-
monkeypatch.setattr("agentwire.send_cli.subprocess.run", lambda *a, **k: has_session)
|
|
203
|
+
self._mock_has_session(monkeypatch)
|
|
197
204
|
monkeypatch.setattr(session_ready, "wait_for_session_ready", lambda s, timeout: True)
|
|
198
205
|
monkeypatch.setattr(session_ready, "send_verified", lambda s, m: True)
|
|
199
206
|
|
|
@@ -201,13 +208,13 @@ class TestCmdSendWaitReady:
|
|
|
201
208
|
payload = self._payload(capsys)
|
|
202
209
|
assert payload["success"] is True
|
|
203
210
|
assert payload["verified"] is True
|
|
211
|
+
assert payload["fallback"] is None
|
|
204
212
|
|
|
205
213
|
def test_not_ready_fails(self, capsys, monkeypatch):
|
|
206
214
|
from agentwire import session_ready
|
|
207
215
|
from agentwire.send_cli import cmd_send
|
|
208
216
|
|
|
209
|
-
|
|
210
|
-
monkeypatch.setattr("agentwire.send_cli.subprocess.run", lambda *a, **k: has_session)
|
|
217
|
+
self._mock_has_session(monkeypatch)
|
|
211
218
|
monkeypatch.setattr(session_ready, "wait_for_session_ready", lambda s, timeout: False)
|
|
212
219
|
|
|
213
220
|
assert cmd_send(self._args()) == 1
|
|
@@ -215,18 +222,56 @@ class TestCmdSendWaitReady:
|
|
|
215
222
|
assert payload["success"] is False
|
|
216
223
|
assert "not ready" in payload["error"]
|
|
217
224
|
|
|
218
|
-
def
|
|
219
|
-
|
|
225
|
+
def test_unverified_falls_back_to_inbox(self, capsys, monkeypatch):
|
|
226
|
+
"""#834: an unverified send must never just hand the problem back to
|
|
227
|
+
whichever caller reads the response — it queues to the durable msg
|
|
228
|
+
inbox so delivery is retried and eventually dead-lettered LOUDLY
|
|
229
|
+
instead of silently depending on the caller noticing and resending."""
|
|
230
|
+
from agentwire import send_cli, session_ready
|
|
220
231
|
from agentwire.send_cli import cmd_send
|
|
221
232
|
|
|
222
|
-
|
|
223
|
-
monkeypatch.setattr("agentwire.send_cli.subprocess.run", lambda *a, **k: has_session)
|
|
233
|
+
self._mock_has_session(monkeypatch)
|
|
224
234
|
monkeypatch.setattr(session_ready, "wait_for_session_ready", lambda s, timeout: True)
|
|
225
235
|
monkeypatch.setattr(session_ready, "send_verified", lambda s, m: False)
|
|
236
|
+
recover = MagicMock(return_value="inbox")
|
|
237
|
+
monkeypatch.setattr(send_cli, "_recover_unverified_send", recover)
|
|
226
238
|
|
|
227
239
|
assert cmd_send(self._args()) == 1
|
|
228
240
|
payload = self._payload(capsys)
|
|
229
241
|
assert payload["verified"] is False
|
|
242
|
+
assert payload["fallback"] == "inbox"
|
|
243
|
+
recover.assert_called_once_with("proj", "my idea", "agentwire")
|
|
244
|
+
|
|
245
|
+
def test_unverified_and_fallback_fails_is_still_reported(self, capsys, monkeypatch):
|
|
246
|
+
from agentwire import send_cli, session_ready
|
|
247
|
+
from agentwire.send_cli import cmd_send
|
|
248
|
+
|
|
249
|
+
self._mock_has_session(monkeypatch)
|
|
250
|
+
monkeypatch.setattr(session_ready, "wait_for_session_ready", lambda s, timeout: True)
|
|
251
|
+
monkeypatch.setattr(session_ready, "send_verified", lambda s, m: False)
|
|
252
|
+
monkeypatch.setattr(send_cli, "_recover_unverified_send", lambda s, m, sender: None)
|
|
253
|
+
|
|
254
|
+
assert cmd_send(self._args()) == 1
|
|
255
|
+
payload = self._payload(capsys)
|
|
256
|
+
assert payload["verified"] is False
|
|
257
|
+
assert payload["fallback"] is None
|
|
258
|
+
|
|
259
|
+
def test_caller_session_arg_wins_over_autodetect(self, capsys, monkeypatch):
|
|
260
|
+
"""#835 review: attribute a fallback's msg-inbox entry to the real
|
|
261
|
+
calling session (threaded via --caller-session from the MCP layer),
|
|
262
|
+
not the generic 'agentwire' -- matters for dead-letter email
|
|
263
|
+
attribution and the rendered [MSG from ...] header."""
|
|
264
|
+
from agentwire import send_cli, session_ready
|
|
265
|
+
from agentwire.send_cli import cmd_send
|
|
266
|
+
|
|
267
|
+
self._mock_has_session(monkeypatch)
|
|
268
|
+
monkeypatch.setattr(session_ready, "wait_for_session_ready", lambda s, timeout: True)
|
|
269
|
+
monkeypatch.setattr(session_ready, "send_verified", lambda s, m: False)
|
|
270
|
+
recover = MagicMock(return_value="inbox")
|
|
271
|
+
monkeypatch.setattr(send_cli, "_recover_unverified_send", recover)
|
|
272
|
+
|
|
273
|
+
cmd_send(self._args(caller_session="council-brain"))
|
|
274
|
+
recover.assert_called_once_with("proj", "my idea", "council-brain")
|
|
230
275
|
|
|
231
276
|
def test_remote_rejected(self, capsys):
|
|
232
277
|
from agentwire.send_cli import cmd_send
|
|
@@ -243,6 +288,114 @@ class TestCmdSendWaitReady:
|
|
|
243
288
|
assert "--pane" in payload["error"]
|
|
244
289
|
|
|
245
290
|
|
|
291
|
+
# --- cmd_send --verify (no --wait-ready) ---
|
|
292
|
+
|
|
293
|
+
class TestCmdSendVerify:
|
|
294
|
+
def _args(self, **overrides):
|
|
295
|
+
defaults = dict(
|
|
296
|
+
session="proj", pane=None, prompt=["my", "idea"],
|
|
297
|
+
json=True, wait_ready=False, verify=True, timeout=None,
|
|
298
|
+
)
|
|
299
|
+
defaults.update(overrides)
|
|
300
|
+
return argparse.Namespace(**defaults)
|
|
301
|
+
|
|
302
|
+
def _payload(self, capsys):
|
|
303
|
+
return json.loads(capsys.readouterr().out.strip())
|
|
304
|
+
|
|
305
|
+
def _mock_has_session(self, monkeypatch):
|
|
306
|
+
has_session = MagicMock(returncode=0)
|
|
307
|
+
monkeypatch.setattr("agentwire.send_cli.subprocess.run", lambda *a, **k: has_session)
|
|
308
|
+
monkeypatch.setattr("agentwire.send_cli.pane_manager.get_current_session", lambda: None)
|
|
309
|
+
|
|
310
|
+
def test_happy_path_verified_skips_fallback(self, capsys, monkeypatch):
|
|
311
|
+
from agentwire import send_cli, session_ready
|
|
312
|
+
from agentwire.send_cli import cmd_send
|
|
313
|
+
|
|
314
|
+
self._mock_has_session(monkeypatch)
|
|
315
|
+
monkeypatch.setattr(session_ready, "send_verified", lambda s, m, retries=1: True)
|
|
316
|
+
recover = MagicMock()
|
|
317
|
+
monkeypatch.setattr(send_cli, "_recover_unverified_send", recover)
|
|
318
|
+
|
|
319
|
+
assert cmd_send(self._args()) == 0
|
|
320
|
+
payload = self._payload(capsys)
|
|
321
|
+
assert payload["verified"] is True
|
|
322
|
+
assert payload["fallback"] is None
|
|
323
|
+
recover.assert_not_called()
|
|
324
|
+
|
|
325
|
+
def test_unverified_falls_back_to_inbox(self, capsys, monkeypatch):
|
|
326
|
+
"""#834: same durable-fallback guarantee as --wait-ready."""
|
|
327
|
+
from agentwire import send_cli, session_ready
|
|
328
|
+
from agentwire.send_cli import cmd_send
|
|
329
|
+
|
|
330
|
+
self._mock_has_session(monkeypatch)
|
|
331
|
+
monkeypatch.setattr(session_ready, "send_verified", lambda s, m, retries=1: False)
|
|
332
|
+
recover = MagicMock(return_value="inbox")
|
|
333
|
+
monkeypatch.setattr(send_cli, "_recover_unverified_send", recover)
|
|
334
|
+
|
|
335
|
+
assert cmd_send(self._args()) == 0 # verify-without-wait-ready reports success=True; verified carries the real state
|
|
336
|
+
payload = self._payload(capsys)
|
|
337
|
+
assert payload["verified"] is False
|
|
338
|
+
assert payload["fallback"] == "inbox"
|
|
339
|
+
recover.assert_called_once_with("proj", "my idea", "agentwire")
|
|
340
|
+
|
|
341
|
+
def test_remote_with_verify_never_touches_the_fallback(self, capsys, monkeypatch):
|
|
342
|
+
"""Remote (session@machine) sends return from an earlier branch
|
|
343
|
+
entirely -- verify=True there only marks the result unverifiable
|
|
344
|
+
across SSH, and must never reach the new fallback machinery."""
|
|
345
|
+
from agentwire import send_cli
|
|
346
|
+
from agentwire.send_cli import cmd_send
|
|
347
|
+
|
|
348
|
+
recover = MagicMock()
|
|
349
|
+
monkeypatch.setattr(send_cli, "_recover_unverified_send", recover)
|
|
350
|
+
monkeypatch.setattr(
|
|
351
|
+
"agentwire.send_cli._run_remote",
|
|
352
|
+
lambda machine_id, cmd: MagicMock(returncode=0),
|
|
353
|
+
)
|
|
354
|
+
monkeypatch.setattr(
|
|
355
|
+
"agentwire.send_cli._get_machine_config",
|
|
356
|
+
lambda machine_id: {"host": "example.com"},
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
assert cmd_send(self._args(session="proj@gpu")) == 0
|
|
360
|
+
payload = self._payload(capsys)
|
|
361
|
+
assert payload["verified"] is None
|
|
362
|
+
assert "fallback" not in payload
|
|
363
|
+
recover.assert_not_called()
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
class TestRecoverUnverifiedSend:
|
|
367
|
+
"""#835 review finding 2: the msg-inbox drain's dedup matches the
|
|
368
|
+
WRAPPED `[MSG from ... ] ... <id>` render, never the bare text a direct
|
|
369
|
+
paste puts on screen -- so blindly enqueuing risks a real duplicate
|
|
370
|
+
delivery when the original send actually landed and only the confirm
|
|
371
|
+
read was ambiguous. _recover_unverified_send closes the common case by
|
|
372
|
+
checking scrollback for the bare message first."""
|
|
373
|
+
|
|
374
|
+
def test_already_on_scrollback_skips_the_inbox_enqueue(self, monkeypatch):
|
|
375
|
+
from agentwire import session_ready
|
|
376
|
+
from agentwire.send_cli import _recover_unverified_send
|
|
377
|
+
|
|
378
|
+
monkeypatch.setattr(session_ready, "scrollback", lambda s, pane_index=0: "...fake capture...")
|
|
379
|
+
monkeypatch.setattr(session_ready, "message_on_scrollback", lambda cap, msg: True)
|
|
380
|
+
recover = MagicMock()
|
|
381
|
+
monkeypatch.setattr(session_ready, "recover_failed_seed", recover)
|
|
382
|
+
|
|
383
|
+
assert _recover_unverified_send("proj", "already sent", "agentwire") == "already_delivered"
|
|
384
|
+
recover.assert_not_called()
|
|
385
|
+
|
|
386
|
+
def test_not_on_scrollback_falls_back_to_the_inbox(self, monkeypatch):
|
|
387
|
+
from agentwire import session_ready
|
|
388
|
+
from agentwire.send_cli import _recover_unverified_send
|
|
389
|
+
|
|
390
|
+
monkeypatch.setattr(session_ready, "scrollback", lambda s, pane_index=0: "...fake capture...")
|
|
391
|
+
monkeypatch.setattr(session_ready, "message_on_scrollback", lambda cap, msg: False)
|
|
392
|
+
recover = MagicMock(return_value="inbox")
|
|
393
|
+
monkeypatch.setattr(session_ready, "recover_failed_seed", recover)
|
|
394
|
+
|
|
395
|
+
assert _recover_unverified_send("proj", "genuinely stuck", "council-brain") == "inbox"
|
|
396
|
+
recover.assert_called_once_with("proj", "genuinely stuck", sender="council-brain")
|
|
397
|
+
|
|
398
|
+
|
|
246
399
|
# --- cmd_new --first-message ---
|
|
247
400
|
|
|
248
401
|
class TestCmdNewFirstMessage:
|
|
@@ -151,6 +151,57 @@ class TestFormatProjectsBehavior:
|
|
|
151
151
|
assert ".agentwire.yml" not in result
|
|
152
152
|
|
|
153
153
|
|
|
154
|
+
class TestDeliveryResultBehavior:
|
|
155
|
+
"""#834: an unverified send must report its inbox fallback, not just
|
|
156
|
+
hand an inert 'check the pane or resend' string back to the caller."""
|
|
157
|
+
|
|
158
|
+
def test_verified_true(self):
|
|
159
|
+
from agentwire.mcp_core import _delivery_result
|
|
160
|
+
result = _delivery_result({"verified": True}, "to session 'proj'")
|
|
161
|
+
assert "verified in pane" in result
|
|
162
|
+
|
|
163
|
+
def test_unverified_with_inbox_fallback_reports_no_action_needed(self):
|
|
164
|
+
from agentwire.mcp_core import _delivery_result
|
|
165
|
+
result = _delivery_result({"verified": False, "fallback": "inbox"}, "to session 'proj'")
|
|
166
|
+
assert "queued to its msg inbox" in result
|
|
167
|
+
assert "No action needed" in result
|
|
168
|
+
|
|
169
|
+
def test_unverified_but_already_delivered_reports_no_action_needed(self):
|
|
170
|
+
"""#835 second-pass review: the 'already_delivered' branch (the
|
|
171
|
+
confirm read was ambiguous but the message was actually on
|
|
172
|
+
scrollback already) had no direct test pinning its wording."""
|
|
173
|
+
from agentwire.mcp_core import _delivery_result
|
|
174
|
+
result = _delivery_result({"verified": False, "fallback": "already_delivered"}, "to session 'proj'")
|
|
175
|
+
assert "already visible on scrollback" in result
|
|
176
|
+
assert "No action needed" in result
|
|
177
|
+
|
|
178
|
+
def test_unverified_with_no_fallback_still_warns(self):
|
|
179
|
+
from agentwire.mcp_core import _delivery_result
|
|
180
|
+
result = _delivery_result({"verified": False, "fallback": None}, "to session 'proj'")
|
|
181
|
+
assert "may be lost" in result
|
|
182
|
+
|
|
183
|
+
def test_pane_send_has_no_fallback_key_and_must_not_claim_one_failed(self):
|
|
184
|
+
"""pane_send's `agentwire send --pane` branch never attempts the
|
|
185
|
+
inbox fallback (the msg inbox only addresses sessions, not panes) —
|
|
186
|
+
it omits the `fallback` key entirely. This must read as the original
|
|
187
|
+
'may have been dropped' warning, not falsely claim a fallback ran
|
|
188
|
+
and failed."""
|
|
189
|
+
from agentwire.mcp_core import _delivery_result
|
|
190
|
+
result = _delivery_result({"verified": False}, "to pane 1")
|
|
191
|
+
assert "may have been dropped" in result
|
|
192
|
+
assert "msg-inbox fallback" not in result
|
|
193
|
+
|
|
194
|
+
def test_remote_unverifiable(self):
|
|
195
|
+
from agentwire.mcp_core import _delivery_result
|
|
196
|
+
result = _delivery_result({"verified": None}, "to session 'proj'")
|
|
197
|
+
assert "can't be verified across SSH" in result
|
|
198
|
+
|
|
199
|
+
def test_no_verified_key_plain_sent(self):
|
|
200
|
+
from agentwire.mcp_core import _delivery_result
|
|
201
|
+
result = _delivery_result({}, "to session 'proj'")
|
|
202
|
+
assert result == "Message sent to session 'proj'."
|
|
203
|
+
|
|
204
|
+
|
|
154
205
|
class TestFormatRolesBehavior:
|
|
155
206
|
def test_full_role_format(self):
|
|
156
207
|
from agentwire.mcp_core import format_roles
|
|
@@ -129,6 +129,10 @@ class TestSessionTools:
|
|
|
129
129
|
sent_msg = args[4] # message follows --verify
|
|
130
130
|
assert '[MESSAGE FROM SESSION "orchestrator"' in sent_msg
|
|
131
131
|
assert 'session_send(session="orchestrator"' in sent_msg
|
|
132
|
+
# #835 review: a msg-inbox fallback attributes to the real caller
|
|
133
|
+
# (dead-letter emails, the rendered header) instead of the generic
|
|
134
|
+
# "agentwire" -- the CLI subprocess can't auto-detect it itself.
|
|
135
|
+
assert args[5:] == ["--caller-session", "orchestrator"]
|
|
132
136
|
|
|
133
137
|
@patch("agentwire.mcp_session.run_agentwire_cmd")
|
|
134
138
|
@patch("agentwire.mcp_session.get_caller_session", return_value=None)
|
|
@@ -68,6 +68,18 @@ class TestHealthEndpoint:
|
|
|
68
68
|
assert data["status"] == "ok"
|
|
69
69
|
assert "version" in data
|
|
70
70
|
|
|
71
|
+
async def test_health_reports_the_real_package_version(self, portal_client):
|
|
72
|
+
"""server.py used to hardcode its own __version__ = "1.3.0",
|
|
73
|
+
disconnected from agentwire/__init__.py and stale since 2026-02 --
|
|
74
|
+
/health silently lied about what was actually running. Pin against
|
|
75
|
+
the real SSOT so a reintroduced hardcode fails this test."""
|
|
76
|
+
from agentwire import __version__ as real_version
|
|
77
|
+
|
|
78
|
+
client, _ = portal_client
|
|
79
|
+
resp = await client.get("/health")
|
|
80
|
+
data = await resp.json()
|
|
81
|
+
assert data["version"] == real_version
|
|
82
|
+
|
|
71
83
|
|
|
72
84
|
class TestPwaSurface:
|
|
73
85
|
"""PWA manifest + service worker + push API (#483)."""
|
|
@@ -280,6 +292,58 @@ class TestApiCreateSession:
|
|
|
280
292
|
if c[0][0] == "notification"]
|
|
281
293
|
assert len(notify_calls) == 1
|
|
282
294
|
|
|
295
|
+
async def test_create_first_message_unverified_but_queued_does_not_say_paste_manually(self, portal_client):
|
|
296
|
+
"""#835: an unverified first-message send that recovered via the
|
|
297
|
+
msg-inbox fallback (or was found already delivered) must not show
|
|
298
|
+
the stale 'paste it manually' toast -- the system already handled
|
|
299
|
+
it, and telling the human to intervene would be actively wrong."""
|
|
300
|
+
import asyncio
|
|
301
|
+
|
|
302
|
+
client, server = portal_client
|
|
303
|
+
|
|
304
|
+
async def cmd_router(args, json_output=True):
|
|
305
|
+
if args[0] == "send":
|
|
306
|
+
return (False, {"error": "Delivery not verified", "fallback": "inbox"})
|
|
307
|
+
return (True, {"session": "ideaproj", "path": "/p"})
|
|
308
|
+
|
|
309
|
+
with patch.object(server, "run_agentwire_cmd", side_effect=cmd_router):
|
|
310
|
+
server.broadcast_dashboard = AsyncMock()
|
|
311
|
+
resp = await client.post("/api/create", json={
|
|
312
|
+
"name": "ideaproj", "first_message": "an idea",
|
|
313
|
+
})
|
|
314
|
+
assert (await resp.json()).get("success") is True
|
|
315
|
+
await asyncio.gather(*server._background_tasks)
|
|
316
|
+
toasts = [n for n in server.active_notifications.values()
|
|
317
|
+
if n["session"] == "ideaproj"]
|
|
318
|
+
assert len(toasts) == 1
|
|
319
|
+
assert "paste it manually" not in toasts[0]["text"]
|
|
320
|
+
assert "queued" in toasts[0]["text"]
|
|
321
|
+
|
|
322
|
+
async def test_create_first_message_already_delivered_does_not_say_paste_manually(self, portal_client):
|
|
323
|
+
"""#835 second-pass review: the 'already_delivered' arm of the same
|
|
324
|
+
ternary had no direct test -- only 'inbox' was exercised."""
|
|
325
|
+
import asyncio
|
|
326
|
+
|
|
327
|
+
client, server = portal_client
|
|
328
|
+
|
|
329
|
+
async def cmd_router(args, json_output=True):
|
|
330
|
+
if args[0] == "send":
|
|
331
|
+
return (False, {"error": "Delivery not verified", "fallback": "already_delivered"})
|
|
332
|
+
return (True, {"session": "ideaproj", "path": "/p"})
|
|
333
|
+
|
|
334
|
+
with patch.object(server, "run_agentwire_cmd", side_effect=cmd_router):
|
|
335
|
+
server.broadcast_dashboard = AsyncMock()
|
|
336
|
+
resp = await client.post("/api/create", json={
|
|
337
|
+
"name": "ideaproj", "first_message": "an idea",
|
|
338
|
+
})
|
|
339
|
+
assert (await resp.json()).get("success") is True
|
|
340
|
+
await asyncio.gather(*server._background_tasks)
|
|
341
|
+
toasts = [n for n in server.active_notifications.values()
|
|
342
|
+
if n["session"] == "ideaproj"]
|
|
343
|
+
assert len(toasts) == 1
|
|
344
|
+
assert "paste it manually" not in toasts[0]["text"]
|
|
345
|
+
assert "delivered" in toasts[0]["text"]
|
|
346
|
+
|
|
283
347
|
async def test_create_without_first_message_no_background_task(self, portal_client):
|
|
284
348
|
client, server = portal_client
|
|
285
349
|
with patch.object(server, "run_agentwire_cmd", new_callable=AsyncMock) as mock_cmd:
|