agentwire-dev 1.60.0__tar.gz → 1.61.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.60.0 → agentwire_dev-1.61.0}/PKG-INFO +1 -1
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/__init__.py +1 -1
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/core.py +69 -20
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/doctor_cli.py +24 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/limits_cli.py +16 -6
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/scheduler/__init__.py +4 -0
- agentwire_dev-1.61.0/agentwire/scheduler/zombie.py +160 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/session_cli.py +92 -42
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/integration/test_worktree_cmd.py +72 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_cli_commands.py +77 -0
- agentwire_dev-1.61.0/tests/unit/test_guarded_launch_command.py +73 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_limits_tick_isolation.py +5 -2
- agentwire_dev-1.61.0/tests/unit/test_scheduler_zombie.py +269 -0
- agentwire_dev-1.61.0/tests/unit/test_session_created_by_env.py +56 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/.agentwire.yml.example +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/.claude/commands/handoff.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/.claude/skills/wiki/SKILL.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/.github/FUNDING.yml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/.github/ISSUE_TEMPLATE/question.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/.github/workflows/pytest.yml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/.github/workflows/ruff.yml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/.github/workflows/security.yml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/.github/workflows/tts-smoke.yml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/.gitignore +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/AGENTS.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/CODE_OF_CONDUCT.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/CONTRIBUTING.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/Dockerfile.local +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/LICENSE +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/README.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/RELEASING.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/SECURITY.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/SPONSORS.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/__main__.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/agents/__init__.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/agents/base.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/agents/tmux.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/cached_status.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/channels/__init__.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/channels/base.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/channels/email.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/channels/push.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/channels/quo.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/channels_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/chrome_tabs.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/completion.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/config.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/config_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/council/__init__.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/council/cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/council/inbox.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/council/minutes.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/council/state.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/council/view.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/devices.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/diff_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/doctor_voice.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/ensure_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/fetch.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/handoff/__init__.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/handoff/git_state.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/handoff/instructions.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/handoff/parser.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/handoff/renderer.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/handoff/schema.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/handoff_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/history.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/history_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/hooks/__init__.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/hooks/agentwire-permission.sh +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/hooks/damage-control/__init__.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/hooks/damage-control/audit_logger.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/hooks/damage-control/bash-tool-damage-control.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/hooks/damage-control/edit-tool-damage-control.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/hooks/damage-control/mcp-tool-damage-control.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/hooks/damage-control/read-tool-damage-control.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/hooks/damage-control/rules/agentwire.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/hooks/damage-control/rules/aws.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/hooks/damage-control/rules/cloud-hosting.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/hooks/damage-control/rules/containers.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/hooks/damage-control/rules/control-plane.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/hooks/damage-control/rules/core.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/hooks/damage-control/rules/databases.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/hooks/damage-control/rules/firebase.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/hooks/damage-control/rules/gcp.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/hooks/damage-control/rules/git.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/hooks/damage-control/rules/gws.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/hooks/damage-control/rules/infrastructure.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/hooks/damage-control/rules/outbound.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/hooks/damage-control/rules/publish.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/hooks/damage-control/rules/remote.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/hooks/damage-control/write-tool-damage-control.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/hooks/idle-handler.sh +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/hooks/queue-processor.sh +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/hooks_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/inbox.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/listen.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/locking.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/machine_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/mcp_channels.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/mcp_chrome.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/mcp_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/mcp_core.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/mcp_council.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/mcp_desktop.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/mcp_handoff.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/mcp_history.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/mcp_listen.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/mcp_lock.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/mcp_machine.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/mcp_msg.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/mcp_notify.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/mcp_pane.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/mcp_projects.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/mcp_scheduler.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/mcp_scratchpad.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/mcp_server.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/mcp_services.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/mcp_session.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/mcp_status.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/mcp_task.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/mcp_tunnels.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/mcp_voice.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/mcp_wiki.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/mcp_worktree.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/msg_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/network.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/notify_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/onboarding.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/palette_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/pane_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/pane_manager.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/portal_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/project_config.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/projects.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/prompt_router.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/prompts_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/push_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/push_store.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/repo_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/research.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/roles/__init__.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/roles/agentwire.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/roles/anchor.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/roles/chatbot.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/roles/contributor.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/roles/correspondent.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/roles/council-brain.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/roles/council-conscience.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/roles/council-critic.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/roles/council-devils-advocate.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/roles/council-gut.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/roles/council-historian.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/roles/council-member.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/roles/council-orchestrator.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/roles/init.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/roles/notifications.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/roles/orchestrator.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/roles/soul.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/roles/task-runner.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/roles/voice.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/roles/worker-worktree.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/roles/worker.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/roles_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/routes/__init__.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/routes/artifacts.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/routes/config.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/routes/council.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/routes/desktop.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/routes/history.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/routes/machines.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/routes/notify.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/routes/palette.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/routes/permission.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/routes/projects.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/routes/push.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/routes/safety.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/routes/scheduler.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/routes/scratchpad.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/routes/sessions.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/routes/sessions_admin.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/routes/voice.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/safety/__init__.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/safety/_core.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/safety_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/safety_commands.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/scheduler/dispatch.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/scheduler/gates.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/scheduler/loop.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/scheduler/models.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/scheduler/report.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/scheduler/schedule.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/scheduler/state.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/scheduler_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/scratchpad.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/security.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/send_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/server.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/services.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/session_context.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/session_ready.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/ssh.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/agentwire-splash-logo-layers--agentwire-text.png +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/agentwire-splash-logo-layers--echo-claw-fg.png +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/agentwire-splash-logo-layers--echo.png +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/agentwire-splash-logo-layers--telephone.png +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/agentwire-splash-logo-layers--tree.png +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/announcements.json +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/apple-touch-icon.png +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/css/desktop.css +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/css/mobile.css +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/favicon-32.png +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/favicon-64.png +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/icons/sessions/bear.webp +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/icons/sessions/cat.webp +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/icons/sessions/crown.webp +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/icons/sessions/custom/agentwire-portal.webp +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/icons/sessions/custom/agentwire-tts.webp +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/icons/sessions/custom/agentwire.webp +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/icons/sessions/deer.webp +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/icons/sessions/drone.webp +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/icons/sessions/eagle.webp +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/icons/sessions/fox.webp +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/icons/sessions/hawk.webp +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/icons/sessions/horse.webp +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/icons/sessions/lion.webp +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/icons/sessions/rabbit.webp +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/icons/sessions/robot.webp +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/icons/sessions/tiger.webp +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/icons/sessions/wolf.webp +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/img/icon-192.png +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/img/icon-512.png +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/.gitkeep +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/announcement-modal.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/api.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/artifact-window.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/collage.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/command-palette.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/council-window.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/dead-key-suppressor.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/desktop-manager.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/desktop.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/help-modal.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/icon-manager.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/mobile.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/notification-prefs.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/notifications-panel.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/ptt.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/push-client.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/review-window.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/safety-shared.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/safety-window.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/scratchpad.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/service-classification.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/session-id.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/session-window.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/shortcuts.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/sidebar/artifacts-section.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/sidebar/config-section.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/sidebar/council-section.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/sidebar/machines-section.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/sidebar/projects-section.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/sidebar/safety-section.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/sidebar/scheduler-section.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/sidebar/services-section.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/sidebar/sessions-section.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/sidebar.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/terminal-font-prefs.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/tile-manager.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/toast.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/token-modal.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/utils/ansi.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/utils/swipe.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/voice/autosend-prefs.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/voice/browser-stt.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/voice/browser-tts.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/voice/jargon.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/voice/prompt.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/js/winbox.bundle.min.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/manifest.webmanifest +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/static/service-worker.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/stt/__init__.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/stt/base.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/stt/cloud.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/stt/engine.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/stt/local.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/stt/server_backend.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/stt/stt_server.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/system_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/tabs_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/tasks.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/tasks_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/templates/__init__.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/templates/base.html +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/templates/council/minutes.html.j2 +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/templates/desktop.html +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/templates/email_notification.html +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/templates/handoff/show-the-story.html.j2 +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/templates/handoff/theme.css.j2 +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/templates/mobile.html +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/templates/pair.html +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/templates/tmux.conf +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/templating.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/tooldefs/aws.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/tooldefs/docker.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/tooldefs/gcp.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/tooldefs/gh.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/tooldefs/git.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/tooldefs/gws.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/tooldefs/kubectl.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/tooldefs/npm.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/tooldefs/terraform.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/tooldefs/uv.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/tts/__init__.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/tts/audio.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/tts/base.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/tts/engines/__init__.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/tts/engines/chatterbox.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/tts/engines/kokoro.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/tts/engines/zonos.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/tts/kokoro_server.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/tts/local.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/tts/registry.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/tts_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/tts_server.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/tunnels.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/tunnels_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/usage_limit.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/utils/__init__.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/utils/chunker.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/utils/event_log.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/utils/file_io.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/utils/paths.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/utils/speech.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/utils/subprocess.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/validation.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/voice_status.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/wiki.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/wiki_audit.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/wiki_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/worktree.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/agentwire/worktree_registry.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/decisions/obsidian-second-brain.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/logo.png +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/INDEX.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/architecture.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/briefing-mode.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/communication/channels.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/communication/hammerspoon.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/communication/handoff.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/concepts.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/council.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/deployment/remote-access.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/deployment/remote-machines.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/glossary.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/integrations/gws-google-workspace-cli.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/internals/damage-control.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/internals/parallel-refactor.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/internals/portal.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/internals/shell-escaping.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/internals/troubleshooting.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/internals/window-collage.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/quickstart.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/research/briefing-mode-feasibility.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/research/orchestration-transport-alternatives.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/scheduling/scheduled-workloads.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/security/damage-control-hardening.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/security/pip-audit.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/security/remote-access-hardening.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/security/secrets.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/services.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/sessions/claude-code-auto-mode.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/sessions/messaging.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/sessions/prompt-routing.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/sessions/window-sizing.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/sessions/worktree-sessions.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/usage-limit-recovery.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/voice/shim-contract.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/voice/stt-cloud.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/voice/stt-self-hosted.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/docs/wiki/voice/tts-self-hosted.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/pyproject.toml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/conftest.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/e2e/test_portal_ui.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/fixtures/sample_agentwire.yml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/fixtures/sample_config.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/fixtures/sample_scheduler.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/integration/test_council_portal.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/integration/test_scheduler_board.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/integration/test_send_verified_tmux.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/integration/test_server_websockets.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_announcements.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_build_agent_command.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_channels.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_chrome_tabs.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_cli_output.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_cli_smoke.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_config.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_config_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_control_plane_protection.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_core_created_by_rooting.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_council_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_council_inbox.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_council_minutes.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_council_state.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_council_view.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_damage_control_bypass.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_damage_control_hooks.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_damage_control_sync.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_dangling_worktrees.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_devices.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_diff_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_doctor_task_migration.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_doctor_voice.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_event_log.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_file_io.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_handoff_git_state.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_handoff_instructions.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_handoff_parser.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_handoff_renderer.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_history.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_hooks_install.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_idle_handler.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_idle_nag.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_inbox.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_init_command.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_install_parity.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_locking.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_mcp_server.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_mcp_tools_args.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_msg_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_notify_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_onboarding_reinit.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_palette_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_pip_environment.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_portal_api.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_posture_default.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_project_config.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_prompt_router.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_push.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_rebuild_guard.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_repo_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_roles.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_routes_snapshot.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_safety_commands.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_safety_disabled_rules.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_safety_escape_hatch.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_safety_heal.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_safety_kill_switch.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_scheduler.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_scheduler_parsing.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_scratchpad.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_security.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_server_async.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_server_pure.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_services.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_session_context.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_session_ready.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_shared_dir_guard.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_shim_selfheal.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_skill_install.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_speech_tags.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_ssh.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_stt_backend.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_stt_cloud.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_stt_engine.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_task_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_tasks.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_tasks_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_templating.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_terminal_resize.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_tmux_template.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_tts_engine_resolution.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_tts_local.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_tunnels_probe.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_unattended_env_isolation.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_usage_limit.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_validation.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_voice_status.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_wiki.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_wiki_audit.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_window_cycling_binding.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_worktree.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_worktree_branch_cleanup.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_worktree_layout.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.61.0}/tests/unit/test_worktree_registry.py +0 -0
|
@@ -148,15 +148,25 @@ def _build_tmux_env_flags_shell(env: dict[str, str]) -> str:
|
|
|
148
148
|
return " ".join(parts) + " "
|
|
149
149
|
|
|
150
150
|
|
|
151
|
-
def _set_session_name_env(agent: "AgentCommand", session_name: str) -> None:
|
|
152
|
-
"""Stamp ``AGENTWIRE_SESSION_NAME``
|
|
151
|
+
def _set_session_name_env(agent: "AgentCommand", session_name: str, created_by: str | None = None) -> None:
|
|
152
|
+
"""Stamp ``AGENTWIRE_SESSION_NAME`` (and, when there's a real parent,
|
|
153
|
+
``AGENTWIRE_CREATED_BY``) onto an ``AgentCommand.env``.
|
|
153
154
|
|
|
154
155
|
Every session created via ``cmd_new`` / ``cmd_spawn`` / ``cmd_recreate``
|
|
155
|
-
/ ``cmd_fork`` / scheduler-spawn paths gets
|
|
156
|
-
(notably the worker damage-control rules in
|
|
157
|
-
can identify which agentwire session the running
|
|
156
|
+
/ ``cmd_fork`` / scheduler-spawn paths gets ``AGENTWIRE_SESSION_NAME`` so
|
|
157
|
+
downstream tooling (notably the worker damage-control rules in
|
|
158
|
+
``safety/_core.py``) can identify which agentwire session the running
|
|
159
|
+
tool is part of.
|
|
160
|
+
|
|
161
|
+
``created_by`` of ``''`` (root/orchestrator) or ``None`` means no
|
|
162
|
+
parent — ``AGENTWIRE_CREATED_BY`` is deliberately left UNSET rather than
|
|
163
|
+
set to an empty string, so the bare pre-agent shell's launch-crash guard
|
|
164
|
+
(``_guarded_launch_command``) can tell "has a parent to escalate to"
|
|
165
|
+
apart from "root session, email the owner" with a plain ``-n`` test (#743).
|
|
158
166
|
"""
|
|
159
167
|
agent.env["AGENTWIRE_SESSION_NAME"] = session_name
|
|
168
|
+
if created_by:
|
|
169
|
+
agent.env["AGENTWIRE_CREATED_BY"] = created_by
|
|
160
170
|
|
|
161
171
|
|
|
162
172
|
def inject_session_env(session: str, env: dict[str, str], remote_host: str | None = None) -> None:
|
|
@@ -847,6 +857,52 @@ def _run_remote(machine_id: str, command: str) -> subprocess.CompletedProcess:
|
|
|
847
857
|
)
|
|
848
858
|
|
|
849
859
|
|
|
860
|
+
def _guarded_launch_command(path_str: str, agent_cmd: str | None) -> str:
|
|
861
|
+
"""Build the pane's ``cd <path> && <agent_cmd>`` line with a missing-dir guard (#739).
|
|
862
|
+
|
|
863
|
+
A crashed worktree create can leave ``path_str`` absent by the time the
|
|
864
|
+
pane actually runs its ``cd`` (race between ``agentwire new`` reporting
|
|
865
|
+
success and the async pane launch, or an external actor removing the
|
|
866
|
+
dir). Without a guard, ``cd`` fails, the shell prints an error and
|
|
867
|
+
carries on, and ``agent_cmd`` (e.g. ``claude``) then launches from the
|
|
868
|
+
WRONG cwd and crashes — dropping the pane to a bare shell that the
|
|
869
|
+
idle-reaper never touches (it only reaps a *running* agent going idle),
|
|
870
|
+
so it lingers forever.
|
|
871
|
+
|
|
872
|
+
On a missing dir this instead alerts and exits the shell, which tears the
|
|
873
|
+
tmux session down instead of leaving a zombie. ``agent_cmd`` never runs.
|
|
874
|
+
|
|
875
|
+
Two alert routes, both shell-runtime-gated on env vars stamped at launch
|
|
876
|
+
(``_set_session_name_env``), so this function stays a pure string builder
|
|
877
|
+
with no Python-level knowledge of the session's parent (#743):
|
|
878
|
+
- A real recorded parent (``$AGENTWIRE_CREATED_BY`` set) gets the crash
|
|
879
|
+
escalated to its msg inbox — in-band, not just an email the human has
|
|
880
|
+
to forward.
|
|
881
|
+
- No parent (root/orchestrator sessions, the genuine scheduler-dispatch
|
|
882
|
+
case) falls back to the original owner email, still gated on
|
|
883
|
+
``$AGENTWIRE_UNATTENDED=1`` (see ``_with_unattended_env``).
|
|
884
|
+
"""
|
|
885
|
+
quoted_path = shlex.quote(path_str)
|
|
886
|
+
session_ref = "${AGENTWIRE_SESSION_NAME:-unknown session}"
|
|
887
|
+
missing_body = f"cd failed at launch: {path_str}"
|
|
888
|
+
notify_parent = (
|
|
889
|
+
'[ -n "$AGENTWIRE_CREATED_BY" ] && agentwire msg send --to "$AGENTWIRE_CREATED_BY" '
|
|
890
|
+
f'--kind escalation --subject "agentwire: worktree missing at launch — {session_ref}" '
|
|
891
|
+
f'--body "{missing_body}" >/dev/null 2>&1'
|
|
892
|
+
)
|
|
893
|
+
notify_owner = (
|
|
894
|
+
'[ -z "$AGENTWIRE_CREATED_BY" ] && [ "$AGENTWIRE_UNATTENDED" = "1" ] && agentwire email '
|
|
895
|
+
f'--subject "agentwire: worktree missing — {session_ref}" '
|
|
896
|
+
f'--body "{missing_body}" >/dev/null 2>&1'
|
|
897
|
+
)
|
|
898
|
+
alert = (
|
|
899
|
+
f"echo \"agentwire: worktree missing at launch, aborting: {path_str}\" >&2; "
|
|
900
|
+
f"{notify_parent}; {notify_owner}"
|
|
901
|
+
)
|
|
902
|
+
guard = f"cd {quoted_path} || {{ {alert}; exit 1; }}"
|
|
903
|
+
return f"{guard} && {agent_cmd}" if agent_cmd else guard
|
|
904
|
+
|
|
905
|
+
|
|
850
906
|
def _launch_tmux_session(
|
|
851
907
|
session_name: str,
|
|
852
908
|
session_path,
|
|
@@ -859,8 +915,9 @@ def _launch_tmux_session(
|
|
|
859
915
|
The one launch sequence shared by ``new`` / ``recreate`` / ``fork`` (#630):
|
|
860
916
|
`tmux new-session -e K=V` injects *env* into the session environment
|
|
861
917
|
BEFORE the initial shell starts (post-hoc `set-environment` never reaches
|
|
862
|
-
it), then `send-keys` cd's into place
|
|
863
|
-
|
|
918
|
+
it), then `send-keys` cd's into place (guarded — see
|
|
919
|
+
``_guarded_launch_command``) and, if *agent_cmd* is non-empty, starts the
|
|
920
|
+
agent after a short settle.
|
|
864
921
|
|
|
865
922
|
Local (machine_id None): runs subprocess calls with check=True (raises on
|
|
866
923
|
tmux failure) and returns None. Remote: runs one composite shell command
|
|
@@ -869,17 +926,14 @@ def _launch_tmux_session(
|
|
|
869
926
|
import time
|
|
870
927
|
|
|
871
928
|
path_str = str(session_path)
|
|
929
|
+
launch_cmd = _guarded_launch_command(path_str, agent_cmd)
|
|
872
930
|
if machine_id:
|
|
873
931
|
env_flags = _build_tmux_env_flags_shell(env)
|
|
874
932
|
create_cmd = (
|
|
875
933
|
f"tmux new-session -d -s {shlex.quote(session_name)} -c {shlex.quote(path_str)} {env_flags}&& "
|
|
876
|
-
f"
|
|
934
|
+
f"sleep 0.1 && "
|
|
935
|
+
f"tmux send-keys -t {shlex.quote(session_name)} {shlex.quote(launch_cmd)} Enter"
|
|
877
936
|
)
|
|
878
|
-
if agent_cmd:
|
|
879
|
-
create_cmd += (
|
|
880
|
-
f" && sleep 0.1 && "
|
|
881
|
-
f"tmux send-keys -t {shlex.quote(session_name)} {shlex.quote(agent_cmd)} Enter"
|
|
882
|
-
)
|
|
883
937
|
return _run_remote(machine_id, create_cmd)
|
|
884
938
|
|
|
885
939
|
subprocess.run(
|
|
@@ -887,16 +941,11 @@ def _launch_tmux_session(
|
|
|
887
941
|
*_build_tmux_env_flags(env)],
|
|
888
942
|
check=True,
|
|
889
943
|
)
|
|
944
|
+
time.sleep(0.1)
|
|
890
945
|
subprocess.run(
|
|
891
|
-
["tmux", "send-keys", "-t", session_name,
|
|
946
|
+
["tmux", "send-keys", "-t", session_name, launch_cmd, "Enter"],
|
|
892
947
|
check=True,
|
|
893
948
|
)
|
|
894
|
-
time.sleep(0.1)
|
|
895
|
-
if agent_cmd:
|
|
896
|
-
subprocess.run(
|
|
897
|
-
["tmux", "send-keys", "-t", session_name, agent_cmd, "Enter"],
|
|
898
|
-
check=True,
|
|
899
|
-
)
|
|
900
949
|
return None
|
|
901
950
|
|
|
902
951
|
|
|
@@ -1008,6 +1008,30 @@ def cmd_doctor(args) -> int:
|
|
|
1008
1008
|
except Exception as e:
|
|
1009
1009
|
print(f" [..] Could not check managed voice shim liveness: {e}")
|
|
1010
1010
|
|
|
1011
|
+
# 14. Zombie scheduler sessions (#739): a worktree dispatch whose launch
|
|
1012
|
+
# crashed before the agent started (e.g. the worktree dir went missing
|
|
1013
|
+
# between `agentwire new` reporting success and the pane's `cd`) drops to
|
|
1014
|
+
# a bare shell the idle-reaper never touches. `agentwire limits tick`
|
|
1015
|
+
# self-heals these every minute, so a live one here means the watchdog
|
|
1016
|
+
# isn't installed/running, not that reaping is broken.
|
|
1017
|
+
print("\nChecking for zombie scheduler sessions (#739)...")
|
|
1018
|
+
try:
|
|
1019
|
+
from .scheduler import scan_zombie_sessions
|
|
1020
|
+
|
|
1021
|
+
zombie_sessions = scan_zombie_sessions()
|
|
1022
|
+
if not zombie_sessions:
|
|
1023
|
+
print(" [ok] No zombie scheduler sessions found")
|
|
1024
|
+
else:
|
|
1025
|
+
issues_found += 1
|
|
1026
|
+
print(f" [!!] {len(zombie_sessions)} scheduler session(s) stuck at a bare shell:")
|
|
1027
|
+
for z in zombie_sessions:
|
|
1028
|
+
print(f" - {z['session']} ({z['command']}, {z['age_seconds']}s old)")
|
|
1029
|
+
print(" `agentwire limits tick` reaps these automatically — "
|
|
1030
|
+
"run it now, or check the usage-limit watchdog is installed "
|
|
1031
|
+
"(`agentwire limits install`).")
|
|
1032
|
+
except Exception as e:
|
|
1033
|
+
print(f" [..] Could not check for zombie scheduler sessions: {e}")
|
|
1034
|
+
|
|
1011
1035
|
# Summary
|
|
1012
1036
|
print()
|
|
1013
1037
|
print("-" * 60)
|
|
@@ -177,16 +177,20 @@ def cmd_limits_tick(args) -> int:
|
|
|
177
177
|
"""One watchdog pass (called by launchd every minute).
|
|
178
178
|
|
|
179
179
|
Runs the usage-limit sweep FIRST, then prompt routing (#276), then the
|
|
180
|
-
polite-message inbox drain (#296), then context auto-management (#442)
|
|
181
|
-
the
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
180
|
+
polite-message inbox drain (#296), then context auto-management (#442),
|
|
181
|
+
then the zombie-scheduler-session reap (#739) — the ordering guarantees a
|
|
182
|
+
usage-limit dialog is parked before any sweep looks at the pane, that the
|
|
183
|
+
inbox only ever delivers to panes the prompt sweep already cleared, that
|
|
184
|
+
auto-``/clear`` runs before the reap so it never fights a pending
|
|
185
|
+
paste/prompt for the same idle, empty box, and that the reap runs LAST
|
|
186
|
+
since it kills sessions outright (nothing later should still be acting on
|
|
187
|
+
one).
|
|
185
188
|
|
|
186
189
|
Each stage runs inside :func:`_run_stage` so an exception in one subsystem
|
|
187
190
|
is logged and skipped rather than aborting the whole cycle (#490).
|
|
188
191
|
"""
|
|
189
192
|
from agentwire import inbox, prompt_router, session_context
|
|
193
|
+
from agentwire.scheduler import zombie as scheduler_zombie
|
|
190
194
|
|
|
191
195
|
result = _run_stage(
|
|
192
196
|
"usage_limit", usage_limit.tick,
|
|
@@ -197,9 +201,12 @@ def cmd_limits_tick(args) -> int:
|
|
|
197
201
|
"inbox", inbox.tick, {"flushed": [], "deferred": []})
|
|
198
202
|
context = _run_stage(
|
|
199
203
|
"session_context", session_context.tick, {"acted": [], "deferred": []})
|
|
204
|
+
zombies = _run_stage(
|
|
205
|
+
"scheduler_zombie", scheduler_zombie.tick, {"killed": []})
|
|
200
206
|
if getattr(args, "json", False):
|
|
201
207
|
print(json.dumps({
|
|
202
|
-
**result, "prompts": prompts, "messages": messages,
|
|
208
|
+
**result, "prompts": prompts, "messages": messages,
|
|
209
|
+
"context": context, "zombies": zombies,
|
|
203
210
|
}))
|
|
204
211
|
return 0
|
|
205
212
|
if result.get("skipped"):
|
|
@@ -237,6 +244,9 @@ def cmd_limits_tick(args) -> int:
|
|
|
237
244
|
if ctx_deferred:
|
|
238
245
|
print("context deferred: " + ", ".join(
|
|
239
246
|
f"{e['session']}({e['deferred']})" for e in ctx_deferred))
|
|
247
|
+
killed = zombies.get("killed") or []
|
|
248
|
+
if killed:
|
|
249
|
+
print("zombie scheduler sessions reaped: " + ", ".join(killed))
|
|
240
250
|
return 0
|
|
241
251
|
|
|
242
252
|
|
|
@@ -24,6 +24,7 @@ from datetime import datetime, timezone
|
|
|
24
24
|
import yaml
|
|
25
25
|
|
|
26
26
|
from ..core import _atomic_write
|
|
27
|
+
from . import zombie
|
|
27
28
|
from .dispatch import (
|
|
28
29
|
_WORKFLOW_STATUS_TO_SCHED,
|
|
29
30
|
_apply_max_runs,
|
|
@@ -110,3 +111,6 @@ from .state import (
|
|
|
110
111
|
load_board,
|
|
111
112
|
save_board,
|
|
112
113
|
)
|
|
114
|
+
from .zombie import reap as reap_zombie_sessions
|
|
115
|
+
from .zombie import scan as scan_zombie_sessions
|
|
116
|
+
from .zombie import tick as zombie_tick
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"""Reap bare-shell scheduler sessions (#739).
|
|
2
|
+
|
|
3
|
+
``_dispatch_worktree_task`` names every worktree branch
|
|
4
|
+
``scheduler-<task>-<ts>``. If the session's launch crashes before the agent
|
|
5
|
+
starts (e.g. the worktree directory went missing between ``agentwire new``
|
|
6
|
+
reporting success and the pane's ``cd``), the tmux session drops to a bare
|
|
7
|
+
shell — which the idle-reaper correctly never touches, since it only reaps a
|
|
8
|
+
*running* agent that goes idle. Nothing else would ever clean that up, so it
|
|
9
|
+
lingers indefinitely. This module finds and kills those sessions.
|
|
10
|
+
|
|
11
|
+
Detected by branch naming rather than ``worktree_registry`` — scheduler
|
|
12
|
+
dispatch goes through ``agentwire new``, which (unlike ``agentwire
|
|
13
|
+
worktree``) never registers there.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import subprocess
|
|
17
|
+
import time
|
|
18
|
+
|
|
19
|
+
from ..worktree import parse_session_name
|
|
20
|
+
|
|
21
|
+
BRANCH_PREFIX = "scheduler-"
|
|
22
|
+
|
|
23
|
+
# tmux `session_created` is whole seconds. A healthy launch's pre-agent shell
|
|
24
|
+
# moment lasts well under a second (see `_launch_tmux_session`'s 0.1s
|
|
25
|
+
# settle), but this gives a slow `claude` cold start real headroom before a
|
|
26
|
+
# session still mid-launch could be mistaken for a zombie.
|
|
27
|
+
MIN_AGE_SECONDS = 60
|
|
28
|
+
|
|
29
|
+
_SHELL_COMMANDS = frozenset({"zsh", "bash", "sh", "fish", "tcsh", "csh", "dash"})
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _is_bare_shell(command: str) -> bool:
|
|
33
|
+
"""True if *command* (a ``pane_current_command`` value) is a login shell."""
|
|
34
|
+
return command.strip().lstrip("-") in _SHELL_COMMANDS
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def scan() -> list[dict]:
|
|
38
|
+
"""Live scheduler-dispatched worktree sessions stuck at a bare shell.
|
|
39
|
+
|
|
40
|
+
Each entry: ``session``, ``branch``, ``command``, ``age_seconds``.
|
|
41
|
+
"""
|
|
42
|
+
result = subprocess.run(
|
|
43
|
+
["tmux", "list-sessions", "-F", "#{session_name}\t#{session_created}"],
|
|
44
|
+
capture_output=True, text=True,
|
|
45
|
+
)
|
|
46
|
+
if result.returncode != 0:
|
|
47
|
+
return []
|
|
48
|
+
|
|
49
|
+
now = time.time()
|
|
50
|
+
zombies = []
|
|
51
|
+
for line in result.stdout.strip().splitlines():
|
|
52
|
+
if "\t" not in line:
|
|
53
|
+
continue
|
|
54
|
+
session, created = line.split("\t", 1)
|
|
55
|
+
_, branch, machine = parse_session_name(session)
|
|
56
|
+
if machine or not branch or not branch.startswith(BRANCH_PREFIX):
|
|
57
|
+
continue
|
|
58
|
+
try:
|
|
59
|
+
age = now - float(created)
|
|
60
|
+
except ValueError:
|
|
61
|
+
continue
|
|
62
|
+
if age < MIN_AGE_SECONDS:
|
|
63
|
+
continue
|
|
64
|
+
|
|
65
|
+
panes = subprocess.run(
|
|
66
|
+
["tmux", "list-panes", "-t", f"={session}", "-F", "#{pane_current_command}"],
|
|
67
|
+
capture_output=True, text=True,
|
|
68
|
+
)
|
|
69
|
+
if panes.returncode != 0:
|
|
70
|
+
continue
|
|
71
|
+
commands = [p for p in panes.stdout.strip().splitlines() if p]
|
|
72
|
+
if len(commands) == 1 and _is_bare_shell(commands[0]):
|
|
73
|
+
zombies.append({
|
|
74
|
+
"session": session, "branch": branch,
|
|
75
|
+
"command": commands[0], "age_seconds": int(age),
|
|
76
|
+
})
|
|
77
|
+
return zombies
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _notify(session: str, branch: str, command: str) -> None:
|
|
81
|
+
"""Alert on a reaped zombie session (#739 + #743).
|
|
82
|
+
|
|
83
|
+
Always posts a portal toast, so the reap is visible in-band rather than
|
|
84
|
+
only in an email the human has to notice and forward. When the session
|
|
85
|
+
has a recorded parent (``created_by`` in its metadata — set at
|
|
86
|
+
``agentwire new`` time, see ``_record_session_creator``), the crash is
|
|
87
|
+
ALSO escalated there via the msg inbox (drained by the same watchdog that
|
|
88
|
+
runs this reap) — a worktree worker's crash reaches its orchestrator, not
|
|
89
|
+
just the owner's inbox. Owner email — the reused Resend wiring, mirrors
|
|
90
|
+
``dispatch._notify_dispatch_timeout`` — is strictly the fallback for a
|
|
91
|
+
session with no recorded parent (the genuine root/scheduler-dispatch
|
|
92
|
+
case #739 originally covered). Each channel is independently
|
|
93
|
+
best-effort; this must never raise into the caller.
|
|
94
|
+
"""
|
|
95
|
+
text = (
|
|
96
|
+
f"agentwire: reaped zombie scheduler session `{session}` (branch "
|
|
97
|
+
f"`{branch}`) — launch crashed at a bare shell (`{command}`) before "
|
|
98
|
+
"the agent started."
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
try:
|
|
102
|
+
from ..core import _post_desktop_notification
|
|
103
|
+
_post_desktop_notification(text, session=session, priority="high")
|
|
104
|
+
except Exception:
|
|
105
|
+
pass
|
|
106
|
+
|
|
107
|
+
parent = None
|
|
108
|
+
try:
|
|
109
|
+
from ..core import load_session_metadata
|
|
110
|
+
parent = load_session_metadata(session).get("created_by") or None
|
|
111
|
+
except Exception:
|
|
112
|
+
parent = None
|
|
113
|
+
|
|
114
|
+
if parent:
|
|
115
|
+
try:
|
|
116
|
+
from ..inbox import enqueue
|
|
117
|
+
enqueue(parent, text, kind="escalation", sender="agentwire")
|
|
118
|
+
except Exception:
|
|
119
|
+
pass
|
|
120
|
+
return
|
|
121
|
+
|
|
122
|
+
try:
|
|
123
|
+
import socket
|
|
124
|
+
|
|
125
|
+
from ..channels.email import send_email
|
|
126
|
+
send_email(
|
|
127
|
+
subject=f"[agentwire] reaped zombie scheduler session: {session}",
|
|
128
|
+
body=(
|
|
129
|
+
f"Scheduler dispatch session `{session}` (branch `{branch}`) "
|
|
130
|
+
f"on `{socket.gethostname()}` never reached its agent — the "
|
|
131
|
+
f"pane was stuck at a bare shell (`{command}`), the #739 "
|
|
132
|
+
"failure mode where the worktree launch crashes before "
|
|
133
|
+
"`claude` starts (e.g. a missing worktree directory). The "
|
|
134
|
+
"watchdog killed the session so it can't linger.\n\n"
|
|
135
|
+
"Check the scheduler events log for the originating "
|
|
136
|
+
"dispatch failure."
|
|
137
|
+
),
|
|
138
|
+
)
|
|
139
|
+
except Exception:
|
|
140
|
+
pass
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def reap() -> dict:
|
|
144
|
+
"""Kill every detected zombie session, logging + emailing each one."""
|
|
145
|
+
from agentwire import scheduler as _sched
|
|
146
|
+
|
|
147
|
+
killed = []
|
|
148
|
+
for z in scan():
|
|
149
|
+
_sched._kill_session(z["session"])
|
|
150
|
+
_sched._log_event("zombie_session_reaped", session=z["session"],
|
|
151
|
+
branch=z["branch"], command=z["command"],
|
|
152
|
+
age_seconds=z["age_seconds"])
|
|
153
|
+
_notify(z["session"], z["branch"], z["command"])
|
|
154
|
+
killed.append(z["session"])
|
|
155
|
+
return {"killed": killed}
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def tick() -> dict:
|
|
159
|
+
"""Watchdog stage entry point — same ``{"killed": [...]}`` shape as ``reap()``."""
|
|
160
|
+
return reap()
|
|
@@ -402,7 +402,17 @@ def cmd_new(args) -> int:
|
|
|
402
402
|
return _output_result(False, json_mode, hint)
|
|
403
403
|
|
|
404
404
|
if not session_path.exists():
|
|
405
|
-
if
|
|
405
|
+
if branch:
|
|
406
|
+
# #739: a worktree session's path is only ever meant to come from
|
|
407
|
+
# `_spawn_worktree`/`ensure_worktree` above. Reaching here with it
|
|
408
|
+
# still missing means that creation silently didn't happen (or
|
|
409
|
+
# the dir vanished right after) — mkdir-ing an empty placeholder
|
|
410
|
+
# would launch the agent into a directory with no git checkout at
|
|
411
|
+
# all. Fail loud instead of returning a `path` that doesn't back
|
|
412
|
+
# a real worktree.
|
|
413
|
+
return _output_result(False, json_mode,
|
|
414
|
+
f"Worktree path does not exist after creation: {session_path}")
|
|
415
|
+
elif args.force or path:
|
|
406
416
|
# Auto-create directory with -f flag or when custom path explicitly provided
|
|
407
417
|
session_path.mkdir(parents=True, exist_ok=True)
|
|
408
418
|
else:
|
|
@@ -445,14 +455,51 @@ def cmd_new(args) -> int:
|
|
|
445
455
|
# Kind default posture (no global default-role lookup).
|
|
446
456
|
posture, _ = _resolve_posture_from_args(args)
|
|
447
457
|
|
|
458
|
+
# Record the creating session as parent for prompt routing. --created-by
|
|
459
|
+
# overrides (including '' to opt out); otherwise default to the caller
|
|
460
|
+
# ONLY when the new session is in the caller's own project — a
|
|
461
|
+
# cross-project spawn gets its own root instead of flattening into the
|
|
462
|
+
# caller's subtree (#715). --caller-session is the candidate caller for
|
|
463
|
+
# that check (MCP forwards it; direct CLI use falls back to the current
|
|
464
|
+
# tmux session). Resolved before building the agent env (#743 needs the
|
|
465
|
+
# real parent, if any, in AGENTWIRE_CREATED_BY BEFORE `tmux new-session
|
|
466
|
+
# -e` fires) and before seeding so a failed seed can name its sender.
|
|
467
|
+
#
|
|
468
|
+
# Joint default with role (#716): an EXPLICITLY requested orchestrator
|
|
469
|
+
# roots by default instead — a durable orchestrator shouldn't answer to
|
|
470
|
+
# whoever happened to spawn it. Gated on the explicit --kind flag (not
|
|
471
|
+
# the resolved `kind`, which is "orchestrator" by default for any
|
|
472
|
+
# branchless name) so the ubiquitous plain `agentwire new` case keeps
|
|
473
|
+
# its existing same-project-inherit behavior untouched. This is the one
|
|
474
|
+
# place this default lives — cmd_worktree's `_launch_session` forwards
|
|
475
|
+
# its own `--kind` straight through as `kind` here, so `agentwire
|
|
476
|
+
# worktree --kind orchestrator` / `agentwire orchestrator` compose with
|
|
477
|
+
# it for free.
|
|
478
|
+
created_by = getattr(args, 'created_by', None)
|
|
479
|
+
caller = None
|
|
480
|
+
if created_by is None and getattr(args, 'kind', None) == 'orchestrator':
|
|
481
|
+
created_by = ''
|
|
482
|
+
elif created_by is None:
|
|
483
|
+
caller = getattr(args, 'caller_session', None) or pane_manager.get_current_session()
|
|
484
|
+
created_by = resolve_default_created_by(caller, session_path)
|
|
485
|
+
|
|
448
486
|
# Build agent command
|
|
449
487
|
model_override = getattr(args, 'model', None)
|
|
450
488
|
agent = build_agent_command(posture, roles if roles else None, model=model_override)
|
|
451
489
|
agent.env.update(parse_env_args(getattr(args, 'env', None)))
|
|
452
|
-
_set_session_name_env(agent, session_name)
|
|
490
|
+
_set_session_name_env(agent, session_name, created_by=created_by)
|
|
453
491
|
|
|
454
492
|
agent_cmd = agent.command
|
|
455
493
|
|
|
494
|
+
# #739: re-verify right before launch — closes the window between the
|
|
495
|
+
# earlier worktree-creation check and this point (shared-dir probe,
|
|
496
|
+
# posture/agent-command resolution) during which an external actor could
|
|
497
|
+
# still remove the directory. `agentwire new --json` must never report
|
|
498
|
+
# success with a `path` that doesn't exist on disk.
|
|
499
|
+
if not session_path.exists():
|
|
500
|
+
return _output_result(False, json_mode,
|
|
501
|
+
f"Session path vanished before launch: {session_path}")
|
|
502
|
+
|
|
456
503
|
# Create new tmux session with env vars injected at creation time, cd
|
|
457
504
|
# into place, and start the agent (skipped when bare).
|
|
458
505
|
_launch_tmux_session(session_name, session_path, agent.env, agent_cmd)
|
|
@@ -479,33 +526,6 @@ def cmd_new(args) -> int:
|
|
|
479
526
|
)
|
|
480
527
|
save_project_config(project_config, session_path)
|
|
481
528
|
|
|
482
|
-
# Record the creating session as parent for prompt routing. --created-by
|
|
483
|
-
# overrides (including '' to opt out); otherwise default to the caller
|
|
484
|
-
# ONLY when the new session is in the caller's own project — a
|
|
485
|
-
# cross-project spawn gets its own root instead of flattening into the
|
|
486
|
-
# caller's subtree (#715). --caller-session is the candidate caller for
|
|
487
|
-
# that check (MCP forwards it; direct CLI use falls back to the current
|
|
488
|
-
# tmux session). Resolved before seeding so a failed seed can name its
|
|
489
|
-
# sender.
|
|
490
|
-
#
|
|
491
|
-
# Joint default with role (#716): an EXPLICITLY requested orchestrator
|
|
492
|
-
# roots by default instead — a durable orchestrator shouldn't answer to
|
|
493
|
-
# whoever happened to spawn it. Gated on the explicit --kind flag (not
|
|
494
|
-
# the resolved `kind`, which is "orchestrator" by default for any
|
|
495
|
-
# branchless name) so the ubiquitous plain `agentwire new` case keeps
|
|
496
|
-
# its existing same-project-inherit behavior untouched. This is the one
|
|
497
|
-
# place this default lives — cmd_worktree's `_launch_session` forwards
|
|
498
|
-
# its own `--kind` straight through as `kind` here, so `agentwire
|
|
499
|
-
# worktree --kind orchestrator` / `agentwire orchestrator` compose with
|
|
500
|
-
# it for free.
|
|
501
|
-
created_by = getattr(args, 'created_by', None)
|
|
502
|
-
caller = None
|
|
503
|
-
if created_by is None and getattr(args, 'kind', None) == 'orchestrator':
|
|
504
|
-
created_by = ''
|
|
505
|
-
elif created_by is None:
|
|
506
|
-
caller = getattr(args, 'caller_session', None) or pane_manager.get_current_session()
|
|
507
|
-
created_by = resolve_default_created_by(caller, session_path)
|
|
508
|
-
|
|
509
529
|
# Deliver the first message once the agent is ready (verified paste).
|
|
510
530
|
# Delivery failure doesn't fail the command — the session exists — but it
|
|
511
531
|
# must be LOUD (#695): clear the partial paste out of the box and fall
|
|
@@ -787,6 +807,24 @@ def cmd_recreate(args) -> int:
|
|
|
787
807
|
return 0
|
|
788
808
|
|
|
789
809
|
|
|
810
|
+
def _resolve_project_arg(project_arg: str, projects_dir: Path) -> Path:
|
|
811
|
+
"""Resolve ``--project`` to an absolute repo path, independent of cwd.
|
|
812
|
+
|
|
813
|
+
An explicit path (absolute, or one that actually exists relative to cwd —
|
|
814
|
+
``.``, ``../sibling``) is honored as given. Otherwise ``project_arg`` is
|
|
815
|
+
treated as a bare project NAME and resolved against the configured
|
|
816
|
+
projects dir, the same way ``cmd_new`` resolves a bare project name from
|
|
817
|
+
``project/branch`` session syntax — never by blindly joining it onto
|
|
818
|
+
whatever directory the command happens to run from. That naive join is
|
|
819
|
+
what path-doubled ``<cwd>/<project>`` into a nonexistent dir when cwd was
|
|
820
|
+
already the project's own directory (#740).
|
|
821
|
+
"""
|
|
822
|
+
expanded = Path(project_arg).expanduser()
|
|
823
|
+
if expanded.is_absolute() or expanded.exists():
|
|
824
|
+
return expanded.resolve()
|
|
825
|
+
return (projects_dir / project_arg).resolve()
|
|
826
|
+
|
|
827
|
+
|
|
790
828
|
def cmd_worktree(args) -> int:
|
|
791
829
|
"""Create a git worktree + agentwire session in one command.
|
|
792
830
|
|
|
@@ -816,13 +854,14 @@ def cmd_worktree(args) -> int:
|
|
|
816
854
|
ref = getattr(args, 'ref', None)
|
|
817
855
|
|
|
818
856
|
from .config import load_config as load_config_typed
|
|
819
|
-
|
|
857
|
+
full_config = load_config_typed()
|
|
858
|
+
wt_config = full_config.worktree
|
|
820
859
|
|
|
821
860
|
# Resolve the repo: explicit --project → config default → git root of cwd
|
|
822
861
|
# (so a worktree session can be spawned from any subdir of a monorepo).
|
|
823
862
|
project_arg = getattr(args, 'project', None)
|
|
824
863
|
if project_arg:
|
|
825
|
-
project_base =
|
|
864
|
+
project_base = _resolve_project_arg(project_arg, full_config.projects.dir)
|
|
826
865
|
elif wt_config.default_project:
|
|
827
866
|
project_base = Path(wt_config.default_project).expanduser().resolve()
|
|
828
867
|
else:
|
|
@@ -846,7 +885,7 @@ def cmd_worktree(args) -> int:
|
|
|
846
885
|
return _worktree_list(args, project_path, json_mode)
|
|
847
886
|
if getattr(args, 'watch', False):
|
|
848
887
|
return _worktree_watch(args, project_path, json_mode)
|
|
849
|
-
if getattr(args, 'prune', False):
|
|
888
|
+
if getattr(args, 'prune', False) or getattr(args, 'gc_merged', False):
|
|
850
889
|
return _worktree_prune(args, project_path, worktree_dir, json_mode)
|
|
851
890
|
if getattr(args, 'dangling', False):
|
|
852
891
|
return _worktree_dangling(args, project_path, json_mode)
|
|
@@ -1233,6 +1272,11 @@ def _worktree_prune(args, project_path: Path, worktree_dir: Path, json_mode: boo
|
|
|
1233
1272
|
branch is already merged" sweep from #717. Off by default: this command
|
|
1234
1273
|
is normally a safe, read-mostly cleanup and shouldn't kill a live,
|
|
1235
1274
|
in-flight session just because its branch happens to look merged.
|
|
1275
|
+
|
|
1276
|
+
``--gc-merged`` alone (no ``--prune``) also dispatches here and runs the
|
|
1277
|
+
same sweep — cmd_worktree treats it as implying --prune (#740), so
|
|
1278
|
+
``--help`` and behavior agree instead of --gc-merged falling through to
|
|
1279
|
+
the usage error.
|
|
1236
1280
|
"""
|
|
1237
1281
|
removed = []
|
|
1238
1282
|
gc_merged_out = []
|
|
@@ -1443,7 +1487,7 @@ def _teardown_entry(
|
|
|
1443
1487
|
project_path: Path, worktree_dir: Path, session_name: str, worktree_path: Path,
|
|
1444
1488
|
branch: str | None, base: str | None, *, keep_branch: bool = False, force_branch: bool = False,
|
|
1445
1489
|
) -> dict:
|
|
1446
|
-
"""Core atomic teardown:
|
|
1490
|
+
"""Core atomic teardown: force-remove the worktree + prune, kill session,
|
|
1447
1491
|
unregister, best-effort branch cleanup. Shared by --remove and
|
|
1448
1492
|
--prune --gc-merged so both paths go through the exact same steps (#717).
|
|
1449
1493
|
|
|
@@ -1451,12 +1495,13 @@ def _teardown_entry(
|
|
|
1451
1495
|
`git worktree remove --force` can't clear it, this fails LOUDLY
|
|
1452
1496
|
(success: False) and leaves the registry entry in place, instead of
|
|
1453
1497
|
silently "unregistering" an orphan.
|
|
1454
|
-
"""
|
|
1455
|
-
killed = False
|
|
1456
|
-
if tmux_session_exists(session_name):
|
|
1457
|
-
subprocess.run(["tmux", "kill-session", "-t", session_name], capture_output=True)
|
|
1458
|
-
killed = True
|
|
1459
1498
|
|
|
1499
|
+
The worktree removal is attempted BEFORE the session is killed, and the
|
|
1500
|
+
session is only killed once removal is confirmed — never the reverse.
|
|
1501
|
+
Killing first would leave a live session's worktree+branch orphaned on
|
|
1502
|
+
disk with no session left to notice, on any removal failure (bad
|
|
1503
|
+
project_path, a stuck worktree, ...) (#740).
|
|
1504
|
+
"""
|
|
1460
1505
|
# Force-remove the git worktree, then prune admin files either way.
|
|
1461
1506
|
_, remove_error = remove_worktree(project_path, worktree_path, force=True)
|
|
1462
1507
|
subprocess.run(["git", "-C", str(project_path), "worktree", "prune"], capture_output=True)
|
|
@@ -1464,7 +1509,12 @@ def _teardown_entry(
|
|
|
1464
1509
|
if worktree_path.exists():
|
|
1465
1510
|
reason = remove_error or "worktree directory still present after `git worktree remove --force`"
|
|
1466
1511
|
return {"success": False, "session": session_name, "path": str(worktree_path),
|
|
1467
|
-
"killed":
|
|
1512
|
+
"killed": False, "worktree_removed": False, "error": reason}
|
|
1513
|
+
|
|
1514
|
+
killed = False
|
|
1515
|
+
if tmux_session_exists(session_name):
|
|
1516
|
+
subprocess.run(["tmux", "kill-session", "-t", session_name], capture_output=True)
|
|
1517
|
+
killed = True
|
|
1468
1518
|
|
|
1469
1519
|
# Removing a project's last worktree also removes the now-empty
|
|
1470
1520
|
# ~/worktrees/<project>/ dir.
|
|
@@ -1490,7 +1540,7 @@ def _teardown_entry(
|
|
|
1490
1540
|
|
|
1491
1541
|
|
|
1492
1542
|
def _worktree_remove(args, project_path: Path, worktree_dir: Path, json_mode: bool) -> int:
|
|
1493
|
-
"""Teardown:
|
|
1543
|
+
"""Teardown: force-remove the worktree + prune, kill the session, unregister,
|
|
1494
1544
|
and (best-effort) delete the branch — one atomic call (#717)."""
|
|
1495
1545
|
name = getattr(args, 'name', None)
|
|
1496
1546
|
if not name:
|
|
@@ -1960,11 +2010,11 @@ def register_session_parser(subparsers) -> None:
|
|
|
1960
2010
|
"parent — a PR nobody is positioned to review/merge (--all for every "
|
|
1961
2011
|
"repo). Distinct from --list's 'orphan' state (dead session, disk "
|
|
1962
2012
|
"remnant) — this flags a LIVE session no one is watching.")
|
|
1963
|
-
parser.add_argument("--remove", action="store_true", help="Atomic teardown:
|
|
2013
|
+
parser.add_argument("--remove", action="store_true", help="Atomic teardown: force-remove the worktree, kill the session, delete the branch once merged, and unregister (cleanup/recovery)")
|
|
1964
2014
|
parser.add_argument("--keep-branch", action="store_true", help="With --remove: skip branch cleanup entirely (default: best-effort delete once confirmed merged)")
|
|
1965
2015
|
parser.add_argument("--force-delete-branch", action="store_true", help="With --remove: delete the branch (local + remote) even if not confirmed merged")
|
|
1966
2016
|
parser.add_argument("--prune", action="store_true", help="Drop registry entries whose worktree is gone + git worktree prune")
|
|
1967
|
-
parser.add_argument("--gc-merged", action="store_true", help="
|
|
2017
|
+
parser.add_argument("--gc-merged", action="store_true", help="Tear down (session/worktree/branch) any registered entry whose branch is confirmed merged; implies --prune (runs standalone too)")
|
|
1968
2018
|
parser.add_argument("--all", action="store_true", help="With --list/--dangling: include worktree sessions across every repo")
|
|
1969
2019
|
_add_posture_flag(parser)
|
|
1970
2020
|
parser.add_argument("--roles", help="Comma-separated roles, STACKED on top of the always-present worker etiquette (kind=orchestrator: REPLACES the default persona instead)")
|