agentwire-dev 1.65.2__tar.gz → 1.66.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.65.2 → agentwire_dev-1.66.0}/PKG-INFO +1 -1
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/__init__.py +1 -1
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/__main__.py +3 -1
- agentwire_dev-1.66.0/agentwire/cohort.py +505 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/core.py +42 -8
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/doctor_cli.py +57 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/ensure_cli.py +75 -2
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/hooks/idle-handler.sh +21 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/inbox.py +39 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/limits_cli.py +18 -9
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/mcp_core.py +28 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/mcp_session.py +61 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/mcp_worktree.py +27 -4
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/pane_cli.py +38 -14
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/pane_manager.py +31 -41
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/project_config.py +67 -8
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/roles/reviewer-worktree.md +2 -0
- agentwire_dev-1.66.0/agentwire/roles/shared-checkout.md +60 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/roles/worker-worktree.md +6 -5
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/scheduler/dispatch.py +35 -11
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/scheduler/zombie.py +38 -7
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/send_cli.py +179 -13
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/server.py +44 -3
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/session_cli.py +489 -134
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/session_ready.py +283 -39
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/tasks.py +9 -0
- agentwire_dev-1.66.0/agentwire/templates/statusline.sh +121 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/templates/tmux.conf +5 -3
- agentwire_dev-1.66.0/agentwire/wait_cli.py +86 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/worktree.py +284 -7
- agentwire_dev-1.66.0/agentwire/worktree_cli.py +152 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/worktree_registry.py +29 -4
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/INDEX.md +3 -1
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/internals/shell-escaping.md +64 -2
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/quickstart.md +17 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/scheduling/scheduled-workloads.md +25 -0
- agentwire_dev-1.66.0/docs/wiki/sessions/fan-out-cohorts.md +182 -0
- agentwire_dev-1.66.0/docs/wiki/sessions/helper-sessions.md +103 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/sessions/messaging.md +36 -1
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/sessions/worktree-sessions.md +20 -2
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/pyproject.toml +1 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/conftest.py +15 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/integration/test_send_verified_tmux.py +194 -5
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/integration/test_worktree_cmd.py +351 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_cli_commands.py +513 -26
- agentwire_dev-1.66.0/tests/unit/test_cohort.py +345 -0
- agentwire_dev-1.66.0/tests/unit/test_ensure_shared_dir.py +135 -0
- agentwire_dev-1.66.0/tests/unit/test_helper_session.py +225 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_idle_handler.py +79 -0
- agentwire_dev-1.66.0/tests/unit/test_launch_via_env.py +106 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_mcp_server.py +62 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_mcp_tools_args.py +19 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_portal_api.py +92 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_project_config.py +99 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_prompt_router.py +15 -2
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_scheduler.py +2 -2
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_scheduler_zombie.py +73 -1
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_session_ready.py +488 -42
- agentwire_dev-1.66.0/tests/unit/test_worktree_dot_project.py +84 -0
- agentwire_dev-1.66.0/tests/unit/test_worktree_git_truth.py +314 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_worktree_layout.py +29 -12
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/.agentwire.yml.example +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/.claude/commands/handoff.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/.claude/skills/wiki/SKILL.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/.github/FUNDING.yml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/.github/ISSUE_TEMPLATE/question.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/.github/workflows/pytest.yml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/.github/workflows/ruff.yml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/.github/workflows/security.yml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/.github/workflows/tts-smoke.yml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/.gitignore +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/AGENTS.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/CODE_OF_CONDUCT.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/CONTRIBUTING.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/Dockerfile.local +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/LICENSE +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/README.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/RELEASING.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/SECURITY.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/SPONSORS.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/agents/__init__.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/agents/base.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/agents/tmux.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/cached_status.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/channels/__init__.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/channels/base.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/channels/email.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/channels/push.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/channels/quo.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/channels_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/chrome_tabs.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/completion.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/config.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/config_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/council/__init__.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/council/cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/council/inbox.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/council/minutes.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/council/state.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/council/view.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/devices.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/diff_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/doctor_voice.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/fetch.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/handoff/__init__.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/handoff/git_state.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/handoff/instructions.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/handoff/parser.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/handoff/renderer.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/handoff/schema.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/handoff_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/history.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/history_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/hooks/__init__.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/hooks/agentwire-permission.sh +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/hooks/damage-control/__init__.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/hooks/damage-control/audit_logger.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/hooks/damage-control/bash-tool-damage-control.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/hooks/damage-control/edit-tool-damage-control.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/hooks/damage-control/mcp-tool-damage-control.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/hooks/damage-control/read-tool-damage-control.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/hooks/damage-control/rules/agentwire.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/hooks/damage-control/rules/aws.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/hooks/damage-control/rules/cloud-hosting.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/hooks/damage-control/rules/containers.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/hooks/damage-control/rules/control-plane.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/hooks/damage-control/rules/core.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/hooks/damage-control/rules/databases.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/hooks/damage-control/rules/firebase.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/hooks/damage-control/rules/gcp.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/hooks/damage-control/rules/git.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/hooks/damage-control/rules/gws.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/hooks/damage-control/rules/infrastructure.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/hooks/damage-control/rules/outbound.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/hooks/damage-control/rules/publish.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/hooks/damage-control/rules/remote.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/hooks/damage-control/write-tool-damage-control.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/hooks/queue-processor.sh +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/hooks_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/listen.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/locking.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/machine_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/mcp_channels.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/mcp_chrome.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/mcp_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/mcp_council.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/mcp_desktop.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/mcp_handoff.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/mcp_history.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/mcp_listen.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/mcp_lock.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/mcp_machine.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/mcp_msg.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/mcp_notify.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/mcp_pane.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/mcp_projects.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/mcp_scheduler.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/mcp_scratchpad.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/mcp_server.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/mcp_services.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/mcp_status.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/mcp_task.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/mcp_tunnels.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/mcp_voice.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/mcp_wiki.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/msg_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/network.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/notify_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/onboarding.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/palette_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/portal_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/projects.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/prompt_router.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/prompts_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/push_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/push_store.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/repo_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/research.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/roles/__init__.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/roles/agentwire.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/roles/anchor.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/roles/chatbot.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/roles/contributor.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/roles/correspondent.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/roles/council-brain.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/roles/council-conscience.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/roles/council-critic.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/roles/council-devils-advocate.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/roles/council-gut.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/roles/council-historian.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/roles/council-member.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/roles/council-orchestrator.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/roles/init.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/roles/notifications.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/roles/orchestrator.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/roles/reviewer.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/roles/soul.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/roles/task-runner.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/roles/voice.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/roles/worker.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/roles_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/routes/__init__.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/routes/artifacts.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/routes/config.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/routes/council.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/routes/desktop.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/routes/history.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/routes/machines.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/routes/notify.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/routes/palette.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/routes/permission.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/routes/projects.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/routes/push.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/routes/safety.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/routes/scheduler.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/routes/scratchpad.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/routes/sessions.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/routes/sessions_admin.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/routes/voice.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/safety/__init__.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/safety/_core.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/safety_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/safety_commands.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/scheduler/__init__.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/scheduler/gates.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/scheduler/loop.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/scheduler/models.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/scheduler/report.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/scheduler/schedule.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/scheduler/state.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/scheduler_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/scratchpad.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/security.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/services.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/session_context.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/ssh.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/agentwire-splash-logo-layers--agentwire-text.png +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/agentwire-splash-logo-layers--echo-claw-fg.png +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/agentwire-splash-logo-layers--echo.png +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/agentwire-splash-logo-layers--telephone.png +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/agentwire-splash-logo-layers--tree.png +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/announcements.json +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/apple-touch-icon.png +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/css/desktop.css +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/css/mobile.css +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/favicon-32.png +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/favicon-64.png +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/icons/sessions/bear.webp +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/icons/sessions/cat.webp +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/icons/sessions/crown.webp +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/icons/sessions/custom/agentwire-portal.webp +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/icons/sessions/custom/agentwire-tts.webp +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/icons/sessions/custom/agentwire.webp +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/icons/sessions/deer.webp +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/icons/sessions/drone.webp +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/icons/sessions/eagle.webp +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/icons/sessions/fox.webp +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/icons/sessions/hawk.webp +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/icons/sessions/horse.webp +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/icons/sessions/lion.webp +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/icons/sessions/rabbit.webp +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/icons/sessions/robot.webp +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/icons/sessions/tiger.webp +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/icons/sessions/wolf.webp +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/img/icon-192.png +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/img/icon-512.png +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/.gitkeep +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/announcement-modal.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/api.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/artifact-window.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/card-terminal.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/collage.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/command-palette.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/council-window.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/dead-key-suppressor.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/desktop-manager.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/desktop.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/help-modal.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/icon-manager.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/lineage.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/mobile.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/notification-prefs.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/notifications-panel.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/ptt.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/push-client.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/review-window.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/safety-shared.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/safety-window.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/scratchpad.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/service-classification.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/session-hud-controller.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/session-hud-notices.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/session-hud-spawn.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/session-hud.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/session-id.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/session-window.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/shortcuts.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/sidebar/artifacts-section.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/sidebar/config-section.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/sidebar/council-section.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/sidebar/machines-section.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/sidebar/projects-section.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/sidebar/safety-section.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/sidebar/scheduler-section.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/sidebar/services-section.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/sidebar/sessions-section.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/sidebar.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/spawn-ghost.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/terminal-font-prefs.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/terminal-pane.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/tile-manager.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/toast.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/token-modal.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/topology-render.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/utils/ansi.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/utils/rich-text.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/utils/swipe.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/voice/autosend-prefs.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/voice/browser-stt.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/voice/browser-tts.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/voice/jargon.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/voice/prompt.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/winbox.bundle.min.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/js/workspace-window.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/manifest.webmanifest +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/service-worker.js +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/static/topology-harness.html +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/stt/__init__.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/stt/base.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/stt/cloud.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/stt/engine.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/stt/local.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/stt/server_backend.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/stt/stt_server.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/system_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/tabs_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/tasks_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/templates/__init__.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/templates/base.html +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/templates/council/minutes.html.j2 +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/templates/desktop.html +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/templates/email_notification.html +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/templates/handoff/show-the-story.html.j2 +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/templates/handoff/theme.css.j2 +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/templates/mobile.html +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/templates/pair.html +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/templating.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/tooldefs/aws.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/tooldefs/docker.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/tooldefs/gcp.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/tooldefs/gh.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/tooldefs/git.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/tooldefs/gws.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/tooldefs/kubectl.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/tooldefs/npm.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/tooldefs/terraform.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/tooldefs/uv.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/tts/__init__.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/tts/audio.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/tts/base.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/tts/engines/__init__.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/tts/engines/chatterbox.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/tts/engines/kokoro.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/tts/engines/zonos.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/tts/kokoro_server.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/tts/local.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/tts/registry.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/tts_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/tts_server.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/tunnels.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/tunnels_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/usage_limit.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/utils/__init__.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/utils/chunker.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/utils/event_log.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/utils/file_io.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/utils/paths.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/utils/speech.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/utils/subprocess.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/validation.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/voice_status.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/wiki.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/wiki_audit.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/agentwire/wiki_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/decisions/obsidian-second-brain.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/design/session-card-fields.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/design/session-card-recommendation.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/logo.png +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/architecture.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/briefing-mode.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/communication/channels.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/communication/hammerspoon.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/communication/handoff.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/concepts.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/council.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/deployment/remote-access.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/deployment/remote-machines.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/glossary.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/integrations/gws-google-workspace-cli.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/internals/damage-control.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/internals/parallel-refactor.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/internals/portal.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/internals/session-topology.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/internals/troubleshooting.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/internals/window-collage.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/research/briefing-mode-feasibility.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/research/orchestration-transport-alternatives.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/security/damage-control-hardening.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/security/pip-audit.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/security/remote-access-hardening.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/security/secrets.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/services.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/sessions/claude-code-auto-mode.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/sessions/prompt-routing.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/sessions/window-sizing.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/usage-limit-recovery.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/voice/shim-contract.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/voice/stt-cloud.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/voice/stt-self-hosted.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/docs/wiki/voice/tts-self-hosted.md +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/e2e/test_portal_ui.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/fixtures/sample_agentwire.yml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/fixtures/sample_config.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/fixtures/sample_scheduler.yaml +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/integration/test_council_portal.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/integration/test_scheduler_board.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/integration/test_server_websockets.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_announcements.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_build_agent_command.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_channels.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_chrome_tabs.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_cli_output.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_cli_smoke.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_config.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_config_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_control_plane_protection.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_core_created_by_rooting.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_council_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_council_inbox.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_council_minutes.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_council_state.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_council_view.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_damage_control_bypass.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_damage_control_hooks.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_damage_control_sync.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_dangling_worktrees.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_devices.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_diff_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_doctor_scheduler_staleness.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_doctor_task_migration.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_doctor_voice.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_event_log.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_file_io.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_guarded_launch_command.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_handoff_git_state.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_handoff_instructions.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_handoff_parser.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_handoff_renderer.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_history.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_hooks_install.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_idle_nag.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_inbox.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_init_command.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_install_parity.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_limits_tick_isolation.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_locking.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_msg_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_notify_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_onboarding_reinit.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_palette_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_pip_environment.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_posture_default.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_projects_registry.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_push.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_rebuild_guard.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_repo_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_rich_text_js.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_roles.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_routes_snapshot.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_safety_commands.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_safety_disabled_rules.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_safety_escape_hatch.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_safety_heal.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_safety_kill_switch.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_scheduler_parsing.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_scratchpad.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_security.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_server_async.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_server_pure.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_services.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_session_context.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_session_created_by_env.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_shared_dir_guard.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_shim_selfheal.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_skill_install.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_speech_tags.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_ssh.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_stt_backend.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_stt_cloud.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_stt_engine.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_task_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_tasks.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_tasks_cli.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_templating.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_terminal_resize.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_tmux_template.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_tts_engine_resolution.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_tts_local.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_tunnels_probe.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_unattended_env_isolation.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_usage_limit.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_validation.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_voice_status.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_wiki.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_wiki_audit.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_window_cycling_binding.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_worktree.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_worktree_branch_cleanup.py +0 -0
- {agentwire_dev-1.65.2 → agentwire_dev-1.66.0}/tests/unit/test_worktree_registry.py +0 -0
|
@@ -190,7 +190,7 @@ Command Categories:
|
|
|
190
190
|
from . import doctor_cli, history_cli, machine_cli, safety_cli
|
|
191
191
|
from . import handoff_cli, hooks_cli, mcp_cli, roles_cli, tunnels_cli
|
|
192
192
|
from . import notify_cli, palette_cli, push_cli, repo_cli, system_cli, wiki_cli
|
|
193
|
-
from . import tabs_cli
|
|
193
|
+
from . import tabs_cli, wait_cli, worktree_cli
|
|
194
194
|
from .council import cli as council_cli
|
|
195
195
|
|
|
196
196
|
_REGISTRARS = [ # noqa: N806 # registry constant; Phase 1 of #495 appends here
|
|
@@ -225,6 +225,8 @@ Command Categories:
|
|
|
225
225
|
config_cli.register_config_parser,
|
|
226
226
|
palette_cli.register_palette_parser,
|
|
227
227
|
tabs_cli.register_tabs_parser,
|
|
228
|
+
wait_cli.register_wait_parser,
|
|
229
|
+
worktree_cli.register_helper_parser,
|
|
228
230
|
]
|
|
229
231
|
for _reg in _REGISTRARS:
|
|
230
232
|
_reg(subparsers)
|
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
"""Fan-out cohort ledger — a parent's outstanding children (#852).
|
|
2
|
+
|
|
3
|
+
A scheduled task that fans out child sessions had no way to wait for them.
|
|
4
|
+
The parent went idle while *waiting*, the idle handler read idle as done, and
|
|
5
|
+
the task was reaped — killing the roll-up the parent existed to write and
|
|
6
|
+
orphaning the children's report-backs into dead-letter (which, for
|
|
7
|
+
load-bearing kinds, also emails the owner about work that succeeded). The
|
|
8
|
+
children themselves were full ``agentwire new`` sessions, so nothing reaped
|
|
9
|
+
them either: a nightly fan-out leaked N sessions per run.
|
|
10
|
+
|
|
11
|
+
**idle != done for a session with outstanding children.** This module is the
|
|
12
|
+
one artifact that makes that knowable, with three consumers:
|
|
13
|
+
|
|
14
|
+
1. ``agentwire wait --children`` (:func:`wait`) — the join primitive. Blocking
|
|
15
|
+
inside a tool call is not idle, so the hook never fires and the agent
|
|
16
|
+
resumes naturally to write its roll-up.
|
|
17
|
+
2. The idle-handler guard — a file-existence + ``jq`` check in the same shape
|
|
18
|
+
as the usage-limit park guard and the #276 prompt-router guard. It reads
|
|
19
|
+
``state == "pending"`` and ``deadline`` directly out of the JSON, so those
|
|
20
|
+
two field names are load-bearing across the language boundary.
|
|
21
|
+
3. The watchdog sweeper (:func:`tick`) — the anti-leak backstop. It reaps a
|
|
22
|
+
cohort whose parent died mid-wait, and reconciles a live parent's ledger so
|
|
23
|
+
the guard self-clears as children exit.
|
|
24
|
+
|
|
25
|
+
**Cohort is not rooting.** ``created_by`` (#715) answers "who has authority /
|
|
26
|
+
where do prompts route", and deliberately returns nothing for a *cross-project*
|
|
27
|
+
spawn. The 2026-08-01 fan-out was three cross-project children and one
|
|
28
|
+
same-project one, so a ``created_by``-derived guard would have protected
|
|
29
|
+
exactly one child and reaped the parent out from under the other three. A
|
|
30
|
+
half-linked fan-out is worse than an unlinked one, so cohort membership is
|
|
31
|
+
keyed off the CALLER, independent of the rooting decision.
|
|
32
|
+
|
|
33
|
+
**Ordering is load-bearing.** ``agentwire kill`` runs ``inbox.gc_sender()``,
|
|
34
|
+
which dead-letters the killed session's still-pending load-bearing outbound
|
|
35
|
+
**and emails the owner**. Kill-before-collect turns every child's ``done``
|
|
36
|
+
report into owner email. So: collect the report, consume it, THEN kill.
|
|
37
|
+
|
|
38
|
+
Everything here fails OPEN: a missing, corrupt, or expired ledger must never be
|
|
39
|
+
able to wedge a task alive forever.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
from __future__ import annotations
|
|
43
|
+
|
|
44
|
+
import json
|
|
45
|
+
import os
|
|
46
|
+
import subprocess
|
|
47
|
+
import tempfile
|
|
48
|
+
import time
|
|
49
|
+
from contextlib import contextmanager
|
|
50
|
+
from pathlib import Path
|
|
51
|
+
|
|
52
|
+
from .utils.event_log import append_event
|
|
53
|
+
|
|
54
|
+
COHORT_ROOT = Path.home() / ".agentwire" / "cohorts"
|
|
55
|
+
EVENTS_FILE = Path.home() / ".agentwire" / "cohort-events.jsonl"
|
|
56
|
+
|
|
57
|
+
# How long a cohort may suppress its parent's idle handling. Past this the
|
|
58
|
+
# guard stops firing, `wait` stops blocking, and outstanding children are
|
|
59
|
+
# killed and reported as failures — a wedged child must not pin a task forever.
|
|
60
|
+
DEFAULT_TTL = 3600
|
|
61
|
+
|
|
62
|
+
# How long past its deadline a ledger survives when the parent is still alive.
|
|
63
|
+
# Covers a parent that fanned out and never called `wait --children`: the guard
|
|
64
|
+
# has already stopped protecting it, and the children get reaped rather than
|
|
65
|
+
# leaked. Generous, because a parent that IS about to call `wait` should find
|
|
66
|
+
# its ledger intact.
|
|
67
|
+
STALE_GRACE = 3600
|
|
68
|
+
|
|
69
|
+
# Child states. `pending` is read by the idle-handler guard's jq — renaming it
|
|
70
|
+
# silently disarms the guard.
|
|
71
|
+
PENDING = "pending"
|
|
72
|
+
REPORTED = "reported" # report collected, child killed
|
|
73
|
+
TIMEOUT = "timeout" # deadline hit with no report; child killed anyway
|
|
74
|
+
GONE = "gone" # child's session vanished before it reported
|
|
75
|
+
|
|
76
|
+
RESOLVED_STATES = (REPORTED, TIMEOUT, GONE)
|
|
77
|
+
|
|
78
|
+
# Topologies, which decide whether resolving a child also tears its session
|
|
79
|
+
# down. A `main`-topology child (`agentwire new`) is the leak this issue is
|
|
80
|
+
# about: nothing reaps it, so the cohort does. A `worktree` child is NOT torn
|
|
81
|
+
# down — it holds a branch and possibly an open PR, whose teardown must follow
|
|
82
|
+
# merge verification (#756), and its session is where a reviewer sends fix-ups.
|
|
83
|
+
# It is still tracked, still reported, and still surfaced when it goes silent;
|
|
84
|
+
# an abandoned one is what `worktree --dangling` / `--list` already flag.
|
|
85
|
+
MAIN = "main"
|
|
86
|
+
WORKTREE = "worktree"
|
|
87
|
+
|
|
88
|
+
# Kinds a child's report-back can arrive as. `note` is included because a child
|
|
89
|
+
# that fails mid-task may downgrade its report; `ingest` is not, since passive
|
|
90
|
+
# messages are pull-only pointers the parent reads separately.
|
|
91
|
+
REPORT_KINDS = ("done", "request", "escalation", "note")
|
|
92
|
+
|
|
93
|
+
_POLL_INTERVAL = 2.0
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
# =============================================================================
|
|
97
|
+
# Ledger I/O
|
|
98
|
+
# =============================================================================
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _now() -> int:
|
|
102
|
+
return int(time.time())
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def log_event(event: str, **fields) -> None:
|
|
106
|
+
append_event(EVENTS_FILE, {"ts": _now(), "event": event, **fields})
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def ledger_path(parent: str) -> Path:
|
|
110
|
+
"""Ledger file for *parent*. Worktree names contain ``/`` and nest a dir."""
|
|
111
|
+
if not parent or parent.startswith("/") or ".." in parent.split("/"):
|
|
112
|
+
raise ValueError(f"invalid session name: {parent!r}")
|
|
113
|
+
path = COHORT_ROOT / f"{parent}.json"
|
|
114
|
+
if not path.resolve().is_relative_to(COHORT_ROOT.resolve()):
|
|
115
|
+
raise ValueError(f"invalid session name: {parent!r}")
|
|
116
|
+
return path
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def load(parent: str) -> dict | None:
|
|
120
|
+
"""The parent's ledger, or None when absent/unreadable/malformed.
|
|
121
|
+
|
|
122
|
+
Fails open on purpose: a corrupt ledger reads as "no cohort", so the guard
|
|
123
|
+
stops suppressing and the parent resumes normal idle handling.
|
|
124
|
+
"""
|
|
125
|
+
try:
|
|
126
|
+
data = json.loads(ledger_path(parent).read_text())
|
|
127
|
+
except (OSError, ValueError):
|
|
128
|
+
return None
|
|
129
|
+
if not isinstance(data, dict) or not isinstance(data.get("children"), list):
|
|
130
|
+
return None
|
|
131
|
+
return data
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _save(parent: str, data: dict) -> None:
|
|
135
|
+
path = ledger_path(parent)
|
|
136
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
137
|
+
fd, tmp = tempfile.mkstemp(prefix=path.name + ".", dir=str(path.parent))
|
|
138
|
+
try:
|
|
139
|
+
with os.fdopen(fd, "w") as f:
|
|
140
|
+
json.dump(data, f, indent=2, sort_keys=True)
|
|
141
|
+
os.replace(tmp, path)
|
|
142
|
+
except Exception:
|
|
143
|
+
try:
|
|
144
|
+
os.unlink(tmp)
|
|
145
|
+
except OSError:
|
|
146
|
+
pass
|
|
147
|
+
raise
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@contextmanager
|
|
151
|
+
def _locked(parent: str):
|
|
152
|
+
"""Serialize a read-modify-write on one parent's ledger.
|
|
153
|
+
|
|
154
|
+
An orchestrator can fire several spawns in a single turn (parallel tool
|
|
155
|
+
calls), and a lost update there means a child is never enrolled — i.e. a
|
|
156
|
+
session that leaks. Same mkdir-based lock the inbox uses; best-effort, so a
|
|
157
|
+
stuck lock degrades to the unsynchronized write rather than blocking a
|
|
158
|
+
spawn.
|
|
159
|
+
"""
|
|
160
|
+
lock = ledger_path(parent).with_suffix(".lock")
|
|
161
|
+
held = False
|
|
162
|
+
for _ in range(50):
|
|
163
|
+
try:
|
|
164
|
+
lock.parent.mkdir(parents=True, exist_ok=True)
|
|
165
|
+
os.mkdir(lock)
|
|
166
|
+
held = True
|
|
167
|
+
break
|
|
168
|
+
except FileExistsError:
|
|
169
|
+
time.sleep(0.1)
|
|
170
|
+
except OSError:
|
|
171
|
+
break
|
|
172
|
+
try:
|
|
173
|
+
yield
|
|
174
|
+
finally:
|
|
175
|
+
if held:
|
|
176
|
+
try:
|
|
177
|
+
os.rmdir(lock)
|
|
178
|
+
except OSError:
|
|
179
|
+
pass
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def discard(parent: str) -> bool:
|
|
183
|
+
"""Delete the parent's ledger. True if one was there."""
|
|
184
|
+
try:
|
|
185
|
+
ledger_path(parent).unlink()
|
|
186
|
+
return True
|
|
187
|
+
except (OSError, ValueError):
|
|
188
|
+
return False
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def enroll(parent: str, child: str, task: str | None = None,
|
|
192
|
+
ttl: int = DEFAULT_TTL, topology: str = MAIN) -> bool:
|
|
193
|
+
"""Record *child* as an outstanding member of *parent*'s cohort.
|
|
194
|
+
|
|
195
|
+
Called automatically by ``agentwire new`` (and therefore ``worktree``)
|
|
196
|
+
whenever there is a caller, so a fan-out author has no bookkeeping to
|
|
197
|
+
forget. Re-enrolling a session already in the ledger is a no-op; a fresh
|
|
198
|
+
fan-out after the previous cohort resolved extends the deadline so the
|
|
199
|
+
second wave gets its own full TTL.
|
|
200
|
+
|
|
201
|
+
*topology* decides whether resolving this child also tears its session
|
|
202
|
+
down — see :data:`WORKTREE`.
|
|
203
|
+
"""
|
|
204
|
+
if not parent or not child or parent == child:
|
|
205
|
+
return False
|
|
206
|
+
try:
|
|
207
|
+
with _locked(parent):
|
|
208
|
+
data = load(parent) or {
|
|
209
|
+
"parent": parent, "task": task, "created_at": _now(), "children": [],
|
|
210
|
+
}
|
|
211
|
+
if any(c.get("session") == child for c in data["children"]):
|
|
212
|
+
return False
|
|
213
|
+
data["children"].append({
|
|
214
|
+
"session": child, "state": PENDING, "report": None,
|
|
215
|
+
"topology": topology, "enrolled_at": _now(),
|
|
216
|
+
})
|
|
217
|
+
data["deadline"] = _now() + ttl
|
|
218
|
+
if task and not data.get("task"):
|
|
219
|
+
data["task"] = task
|
|
220
|
+
_save(parent, data)
|
|
221
|
+
except (OSError, ValueError):
|
|
222
|
+
return False
|
|
223
|
+
log_event("enrolled", parent=parent, child=child, task=task)
|
|
224
|
+
return True
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def pending(parent: str) -> list[str]:
|
|
228
|
+
"""Sessions in *parent*'s cohort that haven't been resolved yet."""
|
|
229
|
+
data = load(parent)
|
|
230
|
+
if not data:
|
|
231
|
+
return []
|
|
232
|
+
return [c["session"] for c in data["children"]
|
|
233
|
+
if c.get("state") == PENDING and c.get("session")]
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def blocking(parent: str, now: int | None = None) -> bool:
|
|
237
|
+
"""Should *parent*'s idle handling be suppressed right now?
|
|
238
|
+
|
|
239
|
+
The Python mirror of the idle-handler's jq guard: pending children AND the
|
|
240
|
+
deadline not yet passed. Both halves matter — the deadline is what stops a
|
|
241
|
+
wedged child from pinning a task alive forever.
|
|
242
|
+
"""
|
|
243
|
+
data = load(parent)
|
|
244
|
+
if not data:
|
|
245
|
+
return False
|
|
246
|
+
if not any(c.get("state") == PENDING for c in data["children"]):
|
|
247
|
+
return False
|
|
248
|
+
return (now if now is not None else _now()) < int(data.get("deadline") or 0)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def _mark(data: dict, child: str, state: str, report: str | None = None) -> None:
|
|
252
|
+
for entry in data["children"]:
|
|
253
|
+
if entry.get("session") == child:
|
|
254
|
+
entry["state"] = state
|
|
255
|
+
if report is not None:
|
|
256
|
+
entry["report"] = report
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
# =============================================================================
|
|
260
|
+
# Session liveness / teardown
|
|
261
|
+
# =============================================================================
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def session_exists(session: str) -> bool:
|
|
265
|
+
try:
|
|
266
|
+
return subprocess.run(
|
|
267
|
+
["tmux", "has-session", "-t", f"={session}"],
|
|
268
|
+
capture_output=True, timeout=5,
|
|
269
|
+
).returncode == 0
|
|
270
|
+
except (OSError, subprocess.SubprocessError):
|
|
271
|
+
return False
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def _kill(session: str) -> bool:
|
|
275
|
+
"""Tear a child session down through the CLI's own kill path.
|
|
276
|
+
|
|
277
|
+
Never the child's own job (a wedged child never goes idle, so child-side
|
|
278
|
+
teardown structurally can't cover the failure path), and never raw tmux —
|
|
279
|
+
the shared path also drops session metadata and GCs the child's leftover
|
|
280
|
+
outbound.
|
|
281
|
+
"""
|
|
282
|
+
try:
|
|
283
|
+
from .pane_cli import kill_local_session
|
|
284
|
+
|
|
285
|
+
return bool(kill_local_session(session).get("success"))
|
|
286
|
+
except Exception:
|
|
287
|
+
return False
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
# =============================================================================
|
|
291
|
+
# Collection + join
|
|
292
|
+
# =============================================================================
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def _harvest(parent: str) -> dict[str, list]:
|
|
296
|
+
"""Pending inbox messages to *parent*, grouped by sender.
|
|
297
|
+
|
|
298
|
+
Read straight off disk rather than waiting for the drain to paste them:
|
|
299
|
+
the parent is mid-tool-call, and a paste into its box is exactly what
|
|
300
|
+
#851 makes unreliable for long reports.
|
|
301
|
+
"""
|
|
302
|
+
try:
|
|
303
|
+
from . import inbox
|
|
304
|
+
|
|
305
|
+
out: dict[str, list] = {}
|
|
306
|
+
for msg in inbox.list_messages(parent):
|
|
307
|
+
if msg.kind in REPORT_KINDS:
|
|
308
|
+
out.setdefault(msg.sender, []).append(msg)
|
|
309
|
+
return out
|
|
310
|
+
except Exception:
|
|
311
|
+
return {}
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def _consume(messages: list) -> str:
|
|
315
|
+
"""Take these messages out of the inbox and return their text.
|
|
316
|
+
|
|
317
|
+
Consuming BEFORE the kill is what keeps ``gc_sender`` from dead-lettering
|
|
318
|
+
the report (and emailing the owner) for a child that reported perfectly.
|
|
319
|
+
"""
|
|
320
|
+
texts = []
|
|
321
|
+
for msg in messages:
|
|
322
|
+
texts.append(msg.text)
|
|
323
|
+
try:
|
|
324
|
+
if msg.path:
|
|
325
|
+
msg.path.unlink()
|
|
326
|
+
except OSError:
|
|
327
|
+
pass
|
|
328
|
+
return "\n".join(texts)
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def collect(parent: str) -> dict | None:
|
|
332
|
+
"""One resolution pass over *parent*'s cohort. Returns the ledger, or None.
|
|
333
|
+
|
|
334
|
+
Per pending child: a report waiting in the parent's inbox is consumed and
|
|
335
|
+
the child torn down (collect → consume → kill, in that order); a child
|
|
336
|
+
whose session has already vanished is marked ``gone``. Past the deadline
|
|
337
|
+
every remaining child is torn down and marked ``timeout`` so the parent's
|
|
338
|
+
summary can name it as a failure instead of the run silently leaking a live
|
|
339
|
+
session. Teardown skips worktree children — see :func:`_teardown`.
|
|
340
|
+
"""
|
|
341
|
+
data = load(parent)
|
|
342
|
+
if not data:
|
|
343
|
+
return None
|
|
344
|
+
outstanding = [c["session"] for c in data["children"]
|
|
345
|
+
if c.get("state") == PENDING and c.get("session")]
|
|
346
|
+
if not outstanding:
|
|
347
|
+
return data
|
|
348
|
+
|
|
349
|
+
expired = _now() >= int(data.get("deadline") or 0)
|
|
350
|
+
reports = _harvest(parent)
|
|
351
|
+
for child in outstanding:
|
|
352
|
+
msgs = reports.get(child)
|
|
353
|
+
if msgs:
|
|
354
|
+
_mark(data, child, REPORTED, _consume(msgs))
|
|
355
|
+
_teardown(data, child)
|
|
356
|
+
log_event("collected", parent=parent, child=child)
|
|
357
|
+
elif not session_exists(child):
|
|
358
|
+
_mark(data, child, GONE)
|
|
359
|
+
log_event("child_gone", parent=parent, child=child)
|
|
360
|
+
elif expired:
|
|
361
|
+
_mark(data, child, TIMEOUT)
|
|
362
|
+
_teardown(data, child)
|
|
363
|
+
log_event("child_timeout", parent=parent, child=child)
|
|
364
|
+
_save(parent, data)
|
|
365
|
+
return data
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def _teardown(data: dict, child: str) -> bool:
|
|
369
|
+
"""Kill a resolved child's session — unless it's on worktree topology.
|
|
370
|
+
|
|
371
|
+
The leak #852 is about is a ``main``-topology child: a full session nothing
|
|
372
|
+
reaps. A worktree child is deliberately left running: its branch and any
|
|
373
|
+
open PR are torn down only after merge verification (#756), a reviewer may
|
|
374
|
+
still need to send it fix-ups, and an abandoned one is already surfaced by
|
|
375
|
+
``worktree --dangling``. Killing it would trade a visible leak for a
|
|
376
|
+
silently destroyed working tree.
|
|
377
|
+
"""
|
|
378
|
+
entry = next((c for c in data["children"] if c.get("session") == child), None)
|
|
379
|
+
if entry is not None and entry.get("topology") == WORKTREE:
|
|
380
|
+
entry["torn_down"] = False
|
|
381
|
+
return False
|
|
382
|
+
killed = _kill(child)
|
|
383
|
+
if entry is not None:
|
|
384
|
+
entry["torn_down"] = killed
|
|
385
|
+
return killed
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
def summarize(data: dict) -> dict:
|
|
389
|
+
"""Caller-facing view of a ledger."""
|
|
390
|
+
children = data.get("children") or []
|
|
391
|
+
return {
|
|
392
|
+
"parent": data.get("parent"),
|
|
393
|
+
"task": data.get("task"),
|
|
394
|
+
"deadline": data.get("deadline"),
|
|
395
|
+
"pending": [c["session"] for c in children if c.get("state") == PENDING],
|
|
396
|
+
"reports": [{"session": c["session"], "report": c.get("report")}
|
|
397
|
+
for c in children if c.get("state") == REPORTED],
|
|
398
|
+
"failed": [{"session": c["session"], "state": c.get("state")}
|
|
399
|
+
for c in children if c.get("state") in (TIMEOUT, GONE)],
|
|
400
|
+
# Resolved but deliberately still running: worktree children, whose
|
|
401
|
+
# branch/PR teardown follows merge verification (#756).
|
|
402
|
+
"left_alive": [c["session"] for c in children
|
|
403
|
+
if c.get("state") in (REPORTED, TIMEOUT)
|
|
404
|
+
and c.get("torn_down") is False],
|
|
405
|
+
"children": children,
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
def wait(parent: str, timeout: float = 600.0,
|
|
410
|
+
poll: float = _POLL_INTERVAL) -> dict:
|
|
411
|
+
"""Block until *parent*'s cohort resolves, this call's *timeout* elapses,
|
|
412
|
+
or the cohort deadline passes.
|
|
413
|
+
|
|
414
|
+
Blocking here happens inside a tool call, which is not idle — so the idle
|
|
415
|
+
hook never fires, the parent is never prompted for a roll-up it can't
|
|
416
|
+
write, and it resumes naturally with every child's report in hand.
|
|
417
|
+
|
|
418
|
+
Bounded and re-callable: a fan-out longer than the calling harness's tool
|
|
419
|
+
timeout just loops. The ledger is deleted once nothing is pending, so the
|
|
420
|
+
idle guard stops suppressing the moment the cohort is done.
|
|
421
|
+
"""
|
|
422
|
+
data = load(parent)
|
|
423
|
+
if not data:
|
|
424
|
+
return {"parent": parent, "cohort": False, "resolved": True,
|
|
425
|
+
"pending": [], "reports": [], "failed": [], "left_alive": [],
|
|
426
|
+
"children": []}
|
|
427
|
+
|
|
428
|
+
deadline = time.time() + max(timeout, 0)
|
|
429
|
+
while True:
|
|
430
|
+
data = collect(parent) or data
|
|
431
|
+
result = summarize(data)
|
|
432
|
+
if not result["pending"]:
|
|
433
|
+
discard(parent)
|
|
434
|
+
log_event("resolved", parent=parent,
|
|
435
|
+
reported=len(result["reports"]), failed=len(result["failed"]))
|
|
436
|
+
return {**result, "cohort": True, "resolved": True}
|
|
437
|
+
if time.time() >= deadline:
|
|
438
|
+
return {**result, "cohort": True, "resolved": False}
|
|
439
|
+
time.sleep(poll)
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
# =============================================================================
|
|
443
|
+
# Watchdog sweep
|
|
444
|
+
# =============================================================================
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
def sweep() -> dict:
|
|
448
|
+
"""Reconcile every ledger on disk. The anti-leak backstop.
|
|
449
|
+
|
|
450
|
+
- **Parent gone** → kill whatever is still pending and delete the ledger.
|
|
451
|
+
This is the crash path (usage limit, guard deadline, ``/exit``) that
|
|
452
|
+
leaks children under every other mechanism, including a child-side
|
|
453
|
+
self-kill: a wedged child never goes idle, so it never self-kills.
|
|
454
|
+
- **Parent alive** → mark pending children whose session has vanished, so
|
|
455
|
+
the idle guard self-clears as the cohort empties.
|
|
456
|
+
- **Past deadline + grace** → the parent fanned out and never joined; kill
|
|
457
|
+
the stragglers and drop the ledger rather than leaving it to suppress
|
|
458
|
+
nothing forever.
|
|
459
|
+
|
|
460
|
+
Teardown here is deliberately session-only: killing a tmux session never
|
|
461
|
+
touches a worktree's branch or its open PR, whose removal must follow merge
|
|
462
|
+
verification (#756) and stays with ``worktree --remove``.
|
|
463
|
+
"""
|
|
464
|
+
reaped: list[dict] = []
|
|
465
|
+
swept: list[str] = []
|
|
466
|
+
if not COHORT_ROOT.exists():
|
|
467
|
+
return {"reaped": reaped, "swept": swept}
|
|
468
|
+
|
|
469
|
+
for path in sorted(p for p in COHORT_ROOT.rglob("*.json") if p.is_file()):
|
|
470
|
+
try:
|
|
471
|
+
parent = str(path.relative_to(COHORT_ROOT))[: -len(".json")]
|
|
472
|
+
except ValueError:
|
|
473
|
+
continue
|
|
474
|
+
data = load(parent)
|
|
475
|
+
if not data:
|
|
476
|
+
continue
|
|
477
|
+
outstanding = [c["session"] for c in data["children"]
|
|
478
|
+
if c.get("state") == PENDING and c.get("session")]
|
|
479
|
+
parent_live = session_exists(parent)
|
|
480
|
+
expired = _now() >= int(data.get("deadline") or 0) + STALE_GRACE
|
|
481
|
+
|
|
482
|
+
if parent_live and not expired:
|
|
483
|
+
changed = False
|
|
484
|
+
for child in outstanding:
|
|
485
|
+
if not session_exists(child):
|
|
486
|
+
_mark(data, child, GONE)
|
|
487
|
+
changed = True
|
|
488
|
+
if changed:
|
|
489
|
+
_save(parent, data)
|
|
490
|
+
swept.append(parent)
|
|
491
|
+
continue
|
|
492
|
+
|
|
493
|
+
for child in outstanding:
|
|
494
|
+
if session_exists(child) and _teardown(data, child):
|
|
495
|
+
reaped.append({"parent": parent, "child": child})
|
|
496
|
+
log_event("orphan_reaped", parent=parent, child=child,
|
|
497
|
+
parent_live=parent_live, expired=expired)
|
|
498
|
+
discard(parent)
|
|
499
|
+
swept.append(parent)
|
|
500
|
+
return {"reaped": reaped, "swept": swept}
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
def tick() -> dict:
|
|
504
|
+
"""Watchdog stage entry point — same shape as :func:`sweep`."""
|
|
505
|
+
return sweep()
|
|
@@ -740,8 +740,13 @@ def _record_session_creator(session_name: str, created_by: str | None, via: str)
|
|
|
740
740
|
|
|
741
741
|
The creator becomes the session's parent for prompt routing
|
|
742
742
|
(prompt_router.resolve_parent), winning over .agentwire.yml `parent:`.
|
|
743
|
+
|
|
744
|
+
``created_by`` of ``''`` means "explicitly rootless" and must still be
|
|
745
|
+
written — otherwise a re-`new`/`recreate` that forces standalone (e.g.
|
|
746
|
+
`--created-by ''`) leaves a stale parent from a prior creation in place,
|
|
747
|
+
since `not created_by` is also true for `''`.
|
|
743
748
|
"""
|
|
744
|
-
if
|
|
749
|
+
if created_by is None or created_by == session_name.split("@")[0]:
|
|
745
750
|
return
|
|
746
751
|
metadata = load_session_metadata(session_name)
|
|
747
752
|
metadata.update({
|
|
@@ -963,6 +968,28 @@ def _guarded_launch_command(path_str: str, agent_cmd: str | None) -> str:
|
|
|
963
968
|
return f"{guard} && {agent_cmd}" if agent_cmd else guard
|
|
964
969
|
|
|
965
970
|
|
|
971
|
+
# The pane's launch command travels as an ENV VAR, not as keystrokes (#856).
|
|
972
|
+
#
|
|
973
|
+
# `send-keys` fires 0.1s after `tmux new-session`, before the shell has put
|
|
974
|
+
# its tty into raw mode, so the keystrokes land in the tty's CANONICAL-mode
|
|
975
|
+
# input buffer — which is capped at 1024 bytes per line on macOS
|
|
976
|
+
# (MAX_CANON/`N_TTY_BUF_SIZE`; Linux is 4096). Everything past the cap is
|
|
977
|
+
# discarded SILENTLY, and since the launch line ends in
|
|
978
|
+
# `--append-system-prompt "$(</tmp/…)"`, a truncated one is syntactically
|
|
979
|
+
# incomplete: zsh sits at a continuation prompt forever, `claude` never runs,
|
|
980
|
+
# and the session is a bare shell that `wait_for_session_ready` can only
|
|
981
|
+
# report as "Agent not running". #742/#743 grew `_guarded_launch_command` by
|
|
982
|
+
# ~700 chars, which pushed long-named worktree sessions (the scheduler's
|
|
983
|
+
# `scheduler-<task>-<timestamp>`, whose path is interpolated FOUR times) over
|
|
984
|
+
# the cap — deterministically, since the length is a pure function of the
|
|
985
|
+
# session name.
|
|
986
|
+
#
|
|
987
|
+
# tmux `-e` is not keyboard input, so it has no such cap. Sending a fixed
|
|
988
|
+
# ~70-char `eval` line instead makes the launch length-independent.
|
|
989
|
+
LAUNCH_CMD_ENV = "AGENTWIRE_LAUNCH_CMD"
|
|
990
|
+
_LAUNCH_EVAL = f'eval "${{{LAUNCH_CMD_ENV}:?agentwire: launch command not injected}}"'
|
|
991
|
+
|
|
992
|
+
|
|
966
993
|
def _launch_tmux_session(
|
|
967
994
|
session_name: str,
|
|
968
995
|
session_path,
|
|
@@ -975,9 +1002,14 @@ def _launch_tmux_session(
|
|
|
975
1002
|
The one launch sequence shared by ``new`` / ``recreate`` / ``fork`` (#630):
|
|
976
1003
|
`tmux new-session -e K=V` injects *env* into the session environment
|
|
977
1004
|
BEFORE the initial shell starts (post-hoc `set-environment` never reaches
|
|
978
|
-
it), then `send-keys`
|
|
979
|
-
``_guarded_launch_command``) and, if *agent_cmd* is
|
|
980
|
-
agent after a short settle.
|
|
1005
|
+
it), then `send-keys` runs the guarded launch line (see
|
|
1006
|
+
``_guarded_launch_command``) which cd's into place and, if *agent_cmd* is
|
|
1007
|
+
non-empty, starts the agent after a short settle.
|
|
1008
|
+
|
|
1009
|
+
That launch line rides in as ``AGENTWIRE_LAUNCH_CMD`` and is `eval`'d by
|
|
1010
|
+
the pane, rather than being typed out in full — see
|
|
1011
|
+
:data:`LAUNCH_CMD_ENV` for why typing it is length-limited and fails
|
|
1012
|
+
silently.
|
|
981
1013
|
|
|
982
1014
|
Local (machine_id None): runs subprocess calls with check=True (raises on
|
|
983
1015
|
tmux failure) and returns None. Remote: runs one composite shell command
|
|
@@ -987,23 +1019,25 @@ def _launch_tmux_session(
|
|
|
987
1019
|
|
|
988
1020
|
path_str = str(session_path)
|
|
989
1021
|
launch_cmd = _guarded_launch_command(path_str, agent_cmd)
|
|
1022
|
+
# Copy, never mutate: callers reuse `agent.env` after the launch.
|
|
1023
|
+
launch_env = {**env, LAUNCH_CMD_ENV: launch_cmd}
|
|
990
1024
|
if machine_id:
|
|
991
|
-
env_flags = _build_tmux_env_flags_shell(
|
|
1025
|
+
env_flags = _build_tmux_env_flags_shell(launch_env)
|
|
992
1026
|
create_cmd = (
|
|
993
1027
|
f"tmux new-session -d -s {shlex.quote(session_name)} -c {shlex.quote(path_str)} {env_flags}&& "
|
|
994
1028
|
f"sleep 0.1 && "
|
|
995
|
-
f"tmux send-keys -t {shlex.quote(session_name)} {shlex.quote(
|
|
1029
|
+
f"tmux send-keys -t {shlex.quote(session_name)} {shlex.quote(_LAUNCH_EVAL)} Enter"
|
|
996
1030
|
)
|
|
997
1031
|
return _run_remote(machine_id, create_cmd)
|
|
998
1032
|
|
|
999
1033
|
subprocess.run(
|
|
1000
1034
|
["tmux", "new-session", "-d", "-s", session_name, "-c", path_str,
|
|
1001
|
-
*_build_tmux_env_flags(
|
|
1035
|
+
*_build_tmux_env_flags(launch_env)],
|
|
1002
1036
|
check=True,
|
|
1003
1037
|
)
|
|
1004
1038
|
time.sleep(0.1)
|
|
1005
1039
|
subprocess.run(
|
|
1006
|
-
["tmux", "send-keys", "-t", session_name,
|
|
1040
|
+
["tmux", "send-keys", "-t", session_name, _LAUNCH_EVAL, "Enter"],
|
|
1007
1041
|
check=True,
|
|
1008
1042
|
)
|
|
1009
1043
|
return None
|