agentwire-dev 1.60.0__tar.gz → 1.62.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.62.0}/PKG-INFO +1 -1
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/__init__.py +1 -1
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/core.py +117 -20
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/doctor_cli.py +24 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/limits_cli.py +16 -6
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/mcp_worktree.py +9 -1
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/pane_cli.py +2 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/routes/notify.py +12 -1
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/routes/projects.py +62 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/routes/sessions_admin.py +4 -4
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/scheduler/__init__.py +4 -0
- agentwire_dev-1.62.0/agentwire/scheduler/zombie.py +160 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/session_cli.py +151 -47
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/css/desktop.css +893 -0
- agentwire_dev-1.62.0/agentwire/static/js/card-terminal.js +260 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/collage.js +110 -5
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/desktop-manager.js +11 -1
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/desktop.js +274 -7
- agentwire_dev-1.62.0/agentwire/static/js/lineage.js +138 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/scratchpad.js +4 -0
- agentwire_dev-1.62.0/agentwire/static/js/session-hud-controller.js +227 -0
- agentwire_dev-1.62.0/agentwire/static/js/session-hud-spawn.js +144 -0
- agentwire_dev-1.62.0/agentwire/static/js/session-hud.js +299 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/session-window.js +97 -569
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/shortcuts.js +1 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/sidebar/config-section.js +37 -2
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/sidebar/sessions-section.js +34 -3
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/sidebar.js +6 -3
- agentwire_dev-1.62.0/agentwire/static/js/spawn-ghost.js +105 -0
- agentwire_dev-1.62.0/agentwire/static/js/terminal-pane.js +779 -0
- agentwire_dev-1.62.0/agentwire/static/js/topology-render.js +610 -0
- agentwire_dev-1.62.0/agentwire/static/js/workspace-window.js +141 -0
- agentwire_dev-1.62.0/agentwire/static/topology-harness.html +120 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/INDEX.md +1 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/internals/portal.md +3 -1
- agentwire_dev-1.62.0/docs/wiki/internals/session-topology.md +93 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/internals/window-collage.md +4 -1
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/sessions/worktree-sessions.md +25 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/integration/test_worktree_cmd.py +182 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_cli_commands.py +81 -0
- agentwire_dev-1.62.0/tests/unit/test_guarded_launch_command.py +73 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_limits_tick_isolation.py +5 -2
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_portal_api.py +51 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_routes_snapshot.py +2 -0
- agentwire_dev-1.62.0/tests/unit/test_scheduler_zombie.py +269 -0
- agentwire_dev-1.62.0/tests/unit/test_session_created_by_env.py +56 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_worktree_branch_cleanup.py +112 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/.agentwire.yml.example +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/.claude/commands/handoff.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/.claude/skills/wiki/SKILL.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/.github/FUNDING.yml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/.github/ISSUE_TEMPLATE/question.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/.github/workflows/pytest.yml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/.github/workflows/ruff.yml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/.github/workflows/security.yml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/.github/workflows/tts-smoke.yml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/.gitignore +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/AGENTS.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/CODE_OF_CONDUCT.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/CONTRIBUTING.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/Dockerfile.local +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/LICENSE +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/README.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/RELEASING.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/SECURITY.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/SPONSORS.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/__main__.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/agents/__init__.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/agents/base.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/agents/tmux.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/cached_status.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/channels/__init__.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/channels/base.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/channels/email.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/channels/push.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/channels/quo.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/channels_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/chrome_tabs.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/completion.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/config.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/config_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/council/__init__.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/council/cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/council/inbox.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/council/minutes.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/council/state.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/council/view.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/devices.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/diff_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/doctor_voice.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/ensure_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/fetch.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/handoff/__init__.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/handoff/git_state.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/handoff/instructions.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/handoff/parser.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/handoff/renderer.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/handoff/schema.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/handoff_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/history.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/history_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/hooks/__init__.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/hooks/agentwire-permission.sh +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/hooks/damage-control/__init__.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/hooks/damage-control/audit_logger.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/hooks/damage-control/bash-tool-damage-control.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/hooks/damage-control/edit-tool-damage-control.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/hooks/damage-control/mcp-tool-damage-control.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/hooks/damage-control/read-tool-damage-control.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/hooks/damage-control/rules/agentwire.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/hooks/damage-control/rules/aws.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/hooks/damage-control/rules/cloud-hosting.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/hooks/damage-control/rules/containers.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/hooks/damage-control/rules/control-plane.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/hooks/damage-control/rules/core.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/hooks/damage-control/rules/databases.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/hooks/damage-control/rules/firebase.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/hooks/damage-control/rules/gcp.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/hooks/damage-control/rules/git.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/hooks/damage-control/rules/gws.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/hooks/damage-control/rules/infrastructure.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/hooks/damage-control/rules/outbound.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/hooks/damage-control/rules/publish.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/hooks/damage-control/rules/remote.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/hooks/damage-control/write-tool-damage-control.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/hooks/idle-handler.sh +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/hooks/queue-processor.sh +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/hooks_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/inbox.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/listen.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/locking.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/machine_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/mcp_channels.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/mcp_chrome.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/mcp_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/mcp_core.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/mcp_council.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/mcp_desktop.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/mcp_handoff.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/mcp_history.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/mcp_listen.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/mcp_lock.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/mcp_machine.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/mcp_msg.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/mcp_notify.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/mcp_pane.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/mcp_projects.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/mcp_scheduler.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/mcp_scratchpad.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/mcp_server.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/mcp_services.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/mcp_session.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/mcp_status.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/mcp_task.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/mcp_tunnels.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/mcp_voice.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/mcp_wiki.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/msg_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/network.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/notify_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/onboarding.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/palette_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/pane_manager.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/portal_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/project_config.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/projects.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/prompt_router.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/prompts_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/push_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/push_store.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/repo_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/research.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/roles/__init__.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/roles/agentwire.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/roles/anchor.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/roles/chatbot.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/roles/contributor.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/roles/correspondent.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/roles/council-brain.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/roles/council-conscience.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/roles/council-critic.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/roles/council-devils-advocate.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/roles/council-gut.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/roles/council-historian.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/roles/council-member.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/roles/council-orchestrator.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/roles/init.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/roles/notifications.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/roles/orchestrator.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/roles/soul.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/roles/task-runner.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/roles/voice.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/roles/worker-worktree.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/roles/worker.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/roles_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/routes/__init__.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/routes/artifacts.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/routes/config.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/routes/council.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/routes/desktop.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/routes/history.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/routes/machines.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/routes/palette.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/routes/permission.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/routes/push.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/routes/safety.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/routes/scheduler.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/routes/scratchpad.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/routes/sessions.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/routes/voice.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/safety/__init__.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/safety/_core.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/safety_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/safety_commands.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/scheduler/dispatch.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/scheduler/gates.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/scheduler/loop.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/scheduler/models.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/scheduler/report.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/scheduler/schedule.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/scheduler/state.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/scheduler_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/scratchpad.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/security.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/send_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/server.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/services.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/session_context.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/session_ready.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/ssh.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/agentwire-splash-logo-layers--agentwire-text.png +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/agentwire-splash-logo-layers--echo-claw-fg.png +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/agentwire-splash-logo-layers--echo.png +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/agentwire-splash-logo-layers--telephone.png +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/agentwire-splash-logo-layers--tree.png +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/announcements.json +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/apple-touch-icon.png +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/css/mobile.css +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/favicon-32.png +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/favicon-64.png +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/icons/sessions/bear.webp +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/icons/sessions/cat.webp +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/icons/sessions/crown.webp +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/icons/sessions/custom/agentwire-portal.webp +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/icons/sessions/custom/agentwire-tts.webp +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/icons/sessions/custom/agentwire.webp +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/icons/sessions/deer.webp +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/icons/sessions/drone.webp +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/icons/sessions/eagle.webp +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/icons/sessions/fox.webp +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/icons/sessions/hawk.webp +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/icons/sessions/horse.webp +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/icons/sessions/lion.webp +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/icons/sessions/rabbit.webp +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/icons/sessions/robot.webp +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/icons/sessions/tiger.webp +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/icons/sessions/wolf.webp +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/img/icon-192.png +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/img/icon-512.png +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/.gitkeep +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/announcement-modal.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/api.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/artifact-window.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/command-palette.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/council-window.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/dead-key-suppressor.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/help-modal.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/icon-manager.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/mobile.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/notification-prefs.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/notifications-panel.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/ptt.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/push-client.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/review-window.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/safety-shared.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/safety-window.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/service-classification.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/session-id.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/sidebar/artifacts-section.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/sidebar/council-section.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/sidebar/machines-section.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/sidebar/projects-section.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/sidebar/safety-section.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/sidebar/scheduler-section.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/sidebar/services-section.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/terminal-font-prefs.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/tile-manager.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/toast.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/token-modal.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/utils/ansi.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/utils/swipe.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/voice/autosend-prefs.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/voice/browser-stt.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/voice/browser-tts.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/voice/jargon.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/voice/prompt.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/js/winbox.bundle.min.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/manifest.webmanifest +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/static/service-worker.js +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/stt/__init__.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/stt/base.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/stt/cloud.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/stt/engine.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/stt/local.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/stt/server_backend.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/stt/stt_server.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/system_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/tabs_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/tasks.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/tasks_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/templates/__init__.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/templates/base.html +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/templates/council/minutes.html.j2 +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/templates/desktop.html +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/templates/email_notification.html +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/templates/handoff/show-the-story.html.j2 +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/templates/handoff/theme.css.j2 +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/templates/mobile.html +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/templates/pair.html +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/templates/tmux.conf +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/templating.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/tooldefs/aws.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/tooldefs/docker.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/tooldefs/gcp.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/tooldefs/gh.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/tooldefs/git.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/tooldefs/gws.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/tooldefs/kubectl.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/tooldefs/npm.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/tooldefs/terraform.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/tooldefs/uv.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/tts/__init__.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/tts/audio.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/tts/base.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/tts/engines/__init__.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/tts/engines/chatterbox.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/tts/engines/kokoro.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/tts/engines/zonos.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/tts/kokoro_server.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/tts/local.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/tts/registry.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/tts_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/tts_server.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/tunnels.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/tunnels_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/usage_limit.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/utils/__init__.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/utils/chunker.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/utils/event_log.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/utils/file_io.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/utils/paths.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/utils/speech.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/utils/subprocess.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/validation.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/voice_status.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/wiki.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/wiki_audit.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/wiki_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/worktree.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/agentwire/worktree_registry.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/decisions/obsidian-second-brain.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/logo.png +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/architecture.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/briefing-mode.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/communication/channels.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/communication/hammerspoon.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/communication/handoff.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/concepts.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/council.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/deployment/remote-access.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/deployment/remote-machines.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/glossary.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/integrations/gws-google-workspace-cli.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/internals/damage-control.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/internals/parallel-refactor.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/internals/shell-escaping.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/internals/troubleshooting.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/quickstart.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/research/briefing-mode-feasibility.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/research/orchestration-transport-alternatives.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/scheduling/scheduled-workloads.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/security/damage-control-hardening.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/security/pip-audit.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/security/remote-access-hardening.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/security/secrets.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/services.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/sessions/claude-code-auto-mode.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/sessions/messaging.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/sessions/prompt-routing.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/sessions/window-sizing.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/usage-limit-recovery.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/voice/shim-contract.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/voice/stt-cloud.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/voice/stt-self-hosted.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/docs/wiki/voice/tts-self-hosted.md +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/pyproject.toml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/conftest.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/e2e/test_portal_ui.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/fixtures/sample_agentwire.yml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/fixtures/sample_config.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/fixtures/sample_scheduler.yaml +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/integration/test_council_portal.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/integration/test_scheduler_board.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/integration/test_send_verified_tmux.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/integration/test_server_websockets.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_announcements.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_build_agent_command.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_channels.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_chrome_tabs.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_cli_output.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_cli_smoke.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_config.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_config_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_control_plane_protection.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_core_created_by_rooting.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_council_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_council_inbox.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_council_minutes.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_council_state.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_council_view.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_damage_control_bypass.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_damage_control_hooks.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_damage_control_sync.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_dangling_worktrees.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_devices.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_diff_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_doctor_task_migration.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_doctor_voice.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_event_log.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_file_io.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_handoff_git_state.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_handoff_instructions.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_handoff_parser.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_handoff_renderer.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_history.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_hooks_install.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_idle_handler.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_idle_nag.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_inbox.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_init_command.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_install_parity.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_locking.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_mcp_server.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_mcp_tools_args.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_msg_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_notify_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_onboarding_reinit.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_palette_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_pip_environment.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_posture_default.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_project_config.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_prompt_router.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_push.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_rebuild_guard.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_repo_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_roles.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_safety_commands.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_safety_disabled_rules.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_safety_escape_hatch.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_safety_heal.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_safety_kill_switch.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_scheduler.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_scheduler_parsing.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_scratchpad.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_security.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_server_async.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_server_pure.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_services.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_session_context.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_session_ready.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_shared_dir_guard.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_shim_selfheal.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_skill_install.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_speech_tags.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_ssh.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_stt_backend.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_stt_cloud.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_stt_engine.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_task_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_tasks.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_tasks_cli.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_templating.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_terminal_resize.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_tmux_template.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_tts_engine_resolution.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_tts_local.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_tunnels_probe.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_unattended_env_isolation.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_usage_limit.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_validation.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_voice_status.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_wiki.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_wiki_audit.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_window_cycling_binding.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_worktree.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.0}/tests/unit/test_worktree_layout.py +0 -0
- {agentwire_dev-1.60.0 → agentwire_dev-1.62.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:
|
|
@@ -730,6 +740,54 @@ def _record_session_creator(session_name: str, created_by: str | None, via: str)
|
|
|
730
740
|
store_session_metadata(session_name, metadata)
|
|
731
741
|
|
|
732
742
|
|
|
743
|
+
def _record_session_role(session_name: str, role: str | None) -> None:
|
|
744
|
+
"""Record the session's ROLE axis (orchestrator/worker) to disk (merge-preserving).
|
|
745
|
+
|
|
746
|
+
Distinct from the etiquette/persona ``roles:`` list in ``.agentwire.yml`` —
|
|
747
|
+
this is the fundamental authority axis derived at creation time (``kind``
|
|
748
|
+
in cmd_new, ``effective_kind`` in cmd_worktree, "worker" for cmd_spawn).
|
|
749
|
+
Read back by list_local_sessions() and the session_created notify lookup.
|
|
750
|
+
"""
|
|
751
|
+
if not role:
|
|
752
|
+
return
|
|
753
|
+
metadata = load_session_metadata(session_name)
|
|
754
|
+
metadata["role"] = role
|
|
755
|
+
store_session_metadata(session_name, metadata)
|
|
756
|
+
|
|
757
|
+
|
|
758
|
+
def notify_portal_session_created(session_name: str, parent: str | None, role: str | None) -> None:
|
|
759
|
+
"""Tell the portal a session was just created, with topology context (#747).
|
|
760
|
+
|
|
761
|
+
Fires immediately from the creating process instead of relying solely on
|
|
762
|
+
the global tmux ``session-created`` hook, which only knows the bare
|
|
763
|
+
session name (no parent/role) and can race a slow first-message delivery
|
|
764
|
+
before metadata is written. Fire-and-forget — failures are silently
|
|
765
|
+
ignored since the portal may not be running.
|
|
766
|
+
"""
|
|
767
|
+
import ssl
|
|
768
|
+
|
|
769
|
+
payload: dict = {"event": "session_created", "session": session_name}
|
|
770
|
+
if parent:
|
|
771
|
+
payload["parent"] = parent
|
|
772
|
+
if role:
|
|
773
|
+
payload["role"] = role
|
|
774
|
+
|
|
775
|
+
try:
|
|
776
|
+
ctx = ssl.create_default_context()
|
|
777
|
+
ctx.check_hostname = False
|
|
778
|
+
ctx.verify_mode = ssl.CERT_NONE
|
|
779
|
+
|
|
780
|
+
req = urllib.request.Request(
|
|
781
|
+
f"{_default_portal_url()}/api/notify",
|
|
782
|
+
data=json.dumps(payload).encode(),
|
|
783
|
+
headers={"Content-Type": "application/json", **_portal_auth_headers()},
|
|
784
|
+
method="POST",
|
|
785
|
+
)
|
|
786
|
+
urllib.request.urlopen(req, timeout=3, context=ctx)
|
|
787
|
+
except Exception:
|
|
788
|
+
pass
|
|
789
|
+
|
|
790
|
+
|
|
733
791
|
def _display_parent(session_name: str, path: str = "") -> "str | None":
|
|
734
792
|
"""The session that should visually own this one in the sidebar.
|
|
735
793
|
|
|
@@ -847,6 +905,52 @@ def _run_remote(machine_id: str, command: str) -> subprocess.CompletedProcess:
|
|
|
847
905
|
)
|
|
848
906
|
|
|
849
907
|
|
|
908
|
+
def _guarded_launch_command(path_str: str, agent_cmd: str | None) -> str:
|
|
909
|
+
"""Build the pane's ``cd <path> && <agent_cmd>`` line with a missing-dir guard (#739).
|
|
910
|
+
|
|
911
|
+
A crashed worktree create can leave ``path_str`` absent by the time the
|
|
912
|
+
pane actually runs its ``cd`` (race between ``agentwire new`` reporting
|
|
913
|
+
success and the async pane launch, or an external actor removing the
|
|
914
|
+
dir). Without a guard, ``cd`` fails, the shell prints an error and
|
|
915
|
+
carries on, and ``agent_cmd`` (e.g. ``claude``) then launches from the
|
|
916
|
+
WRONG cwd and crashes — dropping the pane to a bare shell that the
|
|
917
|
+
idle-reaper never touches (it only reaps a *running* agent going idle),
|
|
918
|
+
so it lingers forever.
|
|
919
|
+
|
|
920
|
+
On a missing dir this instead alerts and exits the shell, which tears the
|
|
921
|
+
tmux session down instead of leaving a zombie. ``agent_cmd`` never runs.
|
|
922
|
+
|
|
923
|
+
Two alert routes, both shell-runtime-gated on env vars stamped at launch
|
|
924
|
+
(``_set_session_name_env``), so this function stays a pure string builder
|
|
925
|
+
with no Python-level knowledge of the session's parent (#743):
|
|
926
|
+
- A real recorded parent (``$AGENTWIRE_CREATED_BY`` set) gets the crash
|
|
927
|
+
escalated to its msg inbox — in-band, not just an email the human has
|
|
928
|
+
to forward.
|
|
929
|
+
- No parent (root/orchestrator sessions, the genuine scheduler-dispatch
|
|
930
|
+
case) falls back to the original owner email, still gated on
|
|
931
|
+
``$AGENTWIRE_UNATTENDED=1`` (see ``_with_unattended_env``).
|
|
932
|
+
"""
|
|
933
|
+
quoted_path = shlex.quote(path_str)
|
|
934
|
+
session_ref = "${AGENTWIRE_SESSION_NAME:-unknown session}"
|
|
935
|
+
missing_body = f"cd failed at launch: {path_str}"
|
|
936
|
+
notify_parent = (
|
|
937
|
+
'[ -n "$AGENTWIRE_CREATED_BY" ] && agentwire msg send --to "$AGENTWIRE_CREATED_BY" '
|
|
938
|
+
f'--kind escalation --subject "agentwire: worktree missing at launch — {session_ref}" '
|
|
939
|
+
f'--body "{missing_body}" >/dev/null 2>&1'
|
|
940
|
+
)
|
|
941
|
+
notify_owner = (
|
|
942
|
+
'[ -z "$AGENTWIRE_CREATED_BY" ] && [ "$AGENTWIRE_UNATTENDED" = "1" ] && agentwire email '
|
|
943
|
+
f'--subject "agentwire: worktree missing — {session_ref}" '
|
|
944
|
+
f'--body "{missing_body}" >/dev/null 2>&1'
|
|
945
|
+
)
|
|
946
|
+
alert = (
|
|
947
|
+
f"echo \"agentwire: worktree missing at launch, aborting: {path_str}\" >&2; "
|
|
948
|
+
f"{notify_parent}; {notify_owner}"
|
|
949
|
+
)
|
|
950
|
+
guard = f"cd {quoted_path} || {{ {alert}; exit 1; }}"
|
|
951
|
+
return f"{guard} && {agent_cmd}" if agent_cmd else guard
|
|
952
|
+
|
|
953
|
+
|
|
850
954
|
def _launch_tmux_session(
|
|
851
955
|
session_name: str,
|
|
852
956
|
session_path,
|
|
@@ -859,8 +963,9 @@ def _launch_tmux_session(
|
|
|
859
963
|
The one launch sequence shared by ``new`` / ``recreate`` / ``fork`` (#630):
|
|
860
964
|
`tmux new-session -e K=V` injects *env* into the session environment
|
|
861
965
|
BEFORE the initial shell starts (post-hoc `set-environment` never reaches
|
|
862
|
-
it), then `send-keys` cd's into place
|
|
863
|
-
|
|
966
|
+
it), then `send-keys` cd's into place (guarded — see
|
|
967
|
+
``_guarded_launch_command``) and, if *agent_cmd* is non-empty, starts the
|
|
968
|
+
agent after a short settle.
|
|
864
969
|
|
|
865
970
|
Local (machine_id None): runs subprocess calls with check=True (raises on
|
|
866
971
|
tmux failure) and returns None. Remote: runs one composite shell command
|
|
@@ -869,17 +974,14 @@ def _launch_tmux_session(
|
|
|
869
974
|
import time
|
|
870
975
|
|
|
871
976
|
path_str = str(session_path)
|
|
977
|
+
launch_cmd = _guarded_launch_command(path_str, agent_cmd)
|
|
872
978
|
if machine_id:
|
|
873
979
|
env_flags = _build_tmux_env_flags_shell(env)
|
|
874
980
|
create_cmd = (
|
|
875
981
|
f"tmux new-session -d -s {shlex.quote(session_name)} -c {shlex.quote(path_str)} {env_flags}&& "
|
|
876
|
-
f"
|
|
982
|
+
f"sleep 0.1 && "
|
|
983
|
+
f"tmux send-keys -t {shlex.quote(session_name)} {shlex.quote(launch_cmd)} Enter"
|
|
877
984
|
)
|
|
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
985
|
return _run_remote(machine_id, create_cmd)
|
|
884
986
|
|
|
885
987
|
subprocess.run(
|
|
@@ -887,16 +989,11 @@ def _launch_tmux_session(
|
|
|
887
989
|
*_build_tmux_env_flags(env)],
|
|
888
990
|
check=True,
|
|
889
991
|
)
|
|
992
|
+
time.sleep(0.1)
|
|
890
993
|
subprocess.run(
|
|
891
|
-
["tmux", "send-keys", "-t", session_name,
|
|
994
|
+
["tmux", "send-keys", "-t", session_name, launch_cmd, "Enter"],
|
|
892
995
|
check=True,
|
|
893
996
|
)
|
|
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
997
|
return None
|
|
901
998
|
|
|
902
999
|
|
|
@@ -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
|
|
|
@@ -225,6 +225,7 @@ def worktree_remove(
|
|
|
225
225
|
project_dir: str = "",
|
|
226
226
|
keep_branch: bool = False,
|
|
227
227
|
force_delete_branch: bool = False,
|
|
228
|
+
close_pr_branch: bool = False,
|
|
228
229
|
) -> str:
|
|
229
230
|
"""Tear down a worktree session: kill the session, remove the worktree + branch, unregister.
|
|
230
231
|
|
|
@@ -245,7 +246,12 @@ def worktree_remove(
|
|
|
245
246
|
project_dir: Path to the git repo (default: server cwd).
|
|
246
247
|
keep_branch: Skip branch cleanup entirely (leave the branch as-is).
|
|
247
248
|
force_delete_branch: Delete the branch even if not confirmed merged —
|
|
248
|
-
use only when you're sure the work is safe to discard.
|
|
249
|
+
use only when you're sure the work is safe to discard. Still
|
|
250
|
+
refuses a branch with an OPEN PR (deleting it would silently
|
|
251
|
+
close the PR) unless close_pr_branch is also set.
|
|
252
|
+
close_pr_branch: Combined with force_delete_branch, also delete a
|
|
253
|
+
branch that has an OPEN PR, closing it. Explicit escape hatch —
|
|
254
|
+
only set this when you mean to close that PR.
|
|
249
255
|
|
|
250
256
|
Returns:
|
|
251
257
|
Success message describing what was removed, or a loud failure
|
|
@@ -258,6 +264,8 @@ def worktree_remove(
|
|
|
258
264
|
args += ["--keep-branch"]
|
|
259
265
|
if force_delete_branch:
|
|
260
266
|
args += ["--force-delete-branch"]
|
|
267
|
+
if close_pr_branch:
|
|
268
|
+
args += ["--close-pr-branch"]
|
|
261
269
|
data = run_agentwire_cmd(args)
|
|
262
270
|
session = data.get("session", name)
|
|
263
271
|
killed = " (killed live session)" if data.get("killed") else ""
|
|
@@ -37,6 +37,7 @@ from .core import (
|
|
|
37
37
|
build_agent_command,
|
|
38
38
|
inject_session_env,
|
|
39
39
|
load_config,
|
|
40
|
+
load_session_metadata,
|
|
40
41
|
parse_env_args,
|
|
41
42
|
tmux_session_exists,
|
|
42
43
|
)
|
|
@@ -172,6 +173,7 @@ def list_local_sessions(show_context: bool = False) -> list[dict]:
|
|
|
172
173
|
"posture": cfg.get("posture"),
|
|
173
174
|
"roles": cfg.get("roles", []),
|
|
174
175
|
"parent": _display_parent(parts[0], path),
|
|
176
|
+
"role": load_session_metadata(parts[0]).get("role"),
|
|
175
177
|
}
|
|
176
178
|
if usage_limit_parked(parts[0]):
|
|
177
179
|
session_info["usage_limit"] = True
|
|
@@ -66,9 +66,20 @@ class NotifyRoutesMixin:
|
|
|
66
66
|
await self.broadcast_dashboard("sessions_update", {"sessions": sessions_data})
|
|
67
67
|
|
|
68
68
|
elif event == "session_created":
|
|
69
|
+
# #747: name/parent/role travel in the payload when the
|
|
70
|
+
# creating process (cmd_new et al.) posts this explicitly —
|
|
71
|
+
# that's authoritative, no race. The bare global tmux hook
|
|
72
|
+
# (session-created, any tmux session) carries only the name,
|
|
73
|
+
# so fall back to a fresh lookup for that case (metadata may
|
|
74
|
+
# not be written yet on a very fast reattach — best-effort).
|
|
69
75
|
self._invalidate_session_caches()
|
|
70
|
-
await self.broadcast_dashboard("session_created", {"session": session})
|
|
71
76
|
sessions_data = await self._get_sessions_data()
|
|
77
|
+
entry = next((s for s in sessions_data if s.get("name") == session), None)
|
|
78
|
+
parent = data.get("parent") or (entry.get("parent") if entry else None)
|
|
79
|
+
role = data.get("role") or (entry.get("role") if entry else None)
|
|
80
|
+
await self.broadcast_dashboard("session_created", {
|
|
81
|
+
"session": session, "name": session, "parent": parent, "role": role,
|
|
82
|
+
})
|
|
72
83
|
await self.broadcast_dashboard("sessions_update", {"sessions": sessions_data})
|
|
73
84
|
|
|
74
85
|
elif event == "pane_died":
|
|
@@ -379,6 +379,66 @@ class ProjectsRoutesMixin:
|
|
|
379
379
|
|
|
380
380
|
return web.json_response({"existing": result.get("existing", [])})
|
|
381
381
|
|
|
382
|
+
async def api_worktree_cleanup(self, request: web.Request) -> web.Response:
|
|
383
|
+
"""Tear down an orphaned worktree (Session HUD ghost card "Clean up").
|
|
384
|
+
|
|
385
|
+
Thin wrapper over the plain `agentwire worktree --remove` form — the
|
|
386
|
+
CLI's own merge/open-PR guard decides whether the branch is also
|
|
387
|
+
deleted; this never escalates to --force-delete-branch.
|
|
388
|
+
|
|
389
|
+
Body: {"name": "<branch or session>", "project": "/path/to/repo"}
|
|
390
|
+
Returns whatever the CLI returns verbatim: on success
|
|
391
|
+
{success, session, path, killed, worktree_removed, branch,
|
|
392
|
+
branch_deleted, branch_note, orphaned_tabs}; on failure
|
|
393
|
+
{success: false, error}.
|
|
394
|
+
"""
|
|
395
|
+
try:
|
|
396
|
+
data = await request.json()
|
|
397
|
+
except Exception:
|
|
398
|
+
return web.json_response({"success": False, "error": "Invalid JSON body"}, status=400)
|
|
399
|
+
|
|
400
|
+
name = (data.get("name") or "").strip()
|
|
401
|
+
project = (data.get("project") or "").strip()
|
|
402
|
+
if not name or not project:
|
|
403
|
+
return web.json_response({"success": False, "error": "name and project are required"}, status=400)
|
|
404
|
+
|
|
405
|
+
# Thin wrapper: the git/registry/branch-safety logic lives in the CLI (SSOT).
|
|
406
|
+
success, result = await self.run_agentwire_cmd(
|
|
407
|
+
["worktree", "--remove", name, "-p", project]
|
|
408
|
+
)
|
|
409
|
+
return web.json_response(result, status=200 if success else 400)
|
|
410
|
+
|
|
411
|
+
async def api_worktree_adopt(self, request: web.Request) -> web.Response:
|
|
412
|
+
"""Spawn a session into an existing worktree (Session HUD ghost card "Adopt").
|
|
413
|
+
|
|
414
|
+
Thin wrapper over `agentwire worktree <name> -p <project> --existing
|
|
415
|
+
[--created-by <createdBy>]` — checks out the branch already on disk
|
|
416
|
+
(no new branch) and, when the dead session's recorded creator is
|
|
417
|
+
known, roots the new session under that same parent so it reports
|
|
418
|
+
back the way the original session would have.
|
|
419
|
+
|
|
420
|
+
Body: {"name": "<branch>", "project": "/path/to/repo", "createdBy": "<session>"}
|
|
421
|
+
Returns whatever the CLI returns verbatim (same shape as `agentwire new`'s
|
|
422
|
+
json — success/session/path/... — or {success: false, error}).
|
|
423
|
+
"""
|
|
424
|
+
try:
|
|
425
|
+
data = await request.json()
|
|
426
|
+
except Exception:
|
|
427
|
+
return web.json_response({"success": False, "error": "Invalid JSON body"}, status=400)
|
|
428
|
+
|
|
429
|
+
name = (data.get("name") or "").strip()
|
|
430
|
+
project = (data.get("project") or "").strip()
|
|
431
|
+
created_by = (data.get("createdBy") or "").strip()
|
|
432
|
+
if not name or not project:
|
|
433
|
+
return web.json_response({"success": False, "error": "name and project are required"}, status=400)
|
|
434
|
+
|
|
435
|
+
args = ["worktree", name, "-p", project, "--existing"]
|
|
436
|
+
if created_by:
|
|
437
|
+
args += ["--created-by", created_by]
|
|
438
|
+
|
|
439
|
+
success, result = await self.run_agentwire_cmd(args)
|
|
440
|
+
return web.json_response(result, status=200 if success else 400)
|
|
441
|
+
|
|
382
442
|
|
|
383
443
|
def register_projects_routes(server, app):
|
|
384
444
|
"""Wire the projects domain's routes onto ``app``."""
|
|
@@ -389,3 +449,5 @@ def register_projects_routes(server, app):
|
|
|
389
449
|
app.router.add_get("/api/session/defaults", server.api_session_defaults)
|
|
390
450
|
app.router.add_get("/api/check-path", server.api_check_path)
|
|
391
451
|
app.router.add_get("/api/check-branches", server.api_check_branches)
|
|
452
|
+
app.router.add_post("/api/worktree/cleanup", server.api_worktree_cleanup)
|
|
453
|
+
app.router.add_post("/api/worktree/adopt", server.api_worktree_adopt)
|
|
@@ -183,10 +183,10 @@ class SessionsAdminRoutesMixin:
|
|
|
183
183
|
yaml_config["voice"] = voice
|
|
184
184
|
await self._write_agentwire_yaml(session_path, yaml_config, machine_id)
|
|
185
185
|
|
|
186
|
-
#
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
186
|
+
# No manual broadcast here (#747): the `agentwire new` subprocess
|
|
187
|
+
# just awaited above already posted the enriched session_created
|
|
188
|
+
# (name/parent/role) + sessions_update to this same portal from
|
|
189
|
+
# inside cmd_new, before returning.
|
|
190
190
|
|
|
191
191
|
# Wait until the tmux pane has actually rendered something. The CLI
|
|
192
192
|
# returns the moment `tmux send-keys` *queues* the agent command, so
|
|
@@ -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()
|