agentwire-dev 1.65.3__tar.gz → 1.67.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.3 → agentwire_dev-1.67.0}/PKG-INFO +2 -2
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/__init__.py +1 -1
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/__main__.py +4 -2
- agentwire_dev-1.67.0/agentwire/cohort.py +505 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/completion.py +25 -2
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/core.py +306 -43
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/doctor_cli.py +171 -10
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/ensure_cli.py +191 -10
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/history_cli.py +30 -1
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/hooks/idle-handler.sh +21 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/inbox.py +119 -11
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/limits_cli.py +18 -9
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/mcp_core.py +5 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/mcp_session.py +56 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/mcp_task.py +5 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/mcp_worktree.py +27 -4
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/pane_cli.py +38 -14
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/pane_manager.py +31 -41
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/project_config.py +67 -8
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/roles/reviewer-worktree.md +2 -0
- agentwire_dev-1.67.0/agentwire/roles/shared-checkout.md +60 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/roles/worker-worktree.md +6 -5
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/routes/scheduler.py +34 -20
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/scheduler/__init__.py +1 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/scheduler/dispatch.py +35 -11
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/scheduler/report.py +83 -1
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/scheduler/zombie.py +39 -8
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/scheduler_cli.py +60 -5
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/send_cli.py +128 -44
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/server.py +33 -3
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/session_cli.py +529 -154
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/session_ready.py +268 -39
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/system_cli.py +3 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/tasks.py +45 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/tasks_cli.py +7 -0
- agentwire_dev-1.67.0/agentwire/templates/statusline.sh +121 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/templates/tmux.conf +5 -3
- agentwire_dev-1.67.0/agentwire/wait_cli.py +86 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/worktree.py +300 -7
- agentwire_dev-1.67.0/agentwire/worktree_cli.py +152 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/worktree_registry.py +29 -4
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/INDEX.md +4 -1
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/internals/shell-escaping.md +64 -2
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/quickstart.md +17 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/scheduling/scheduled-workloads.md +170 -1
- agentwire_dev-1.67.0/docs/wiki/sessions/conversation-identity.md +151 -0
- agentwire_dev-1.67.0/docs/wiki/sessions/fan-out-cohorts.md +182 -0
- agentwire_dev-1.67.0/docs/wiki/sessions/helper-sessions.md +103 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/sessions/messaging.md +68 -10
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/sessions/worktree-sessions.md +20 -2
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/pyproject.toml +6 -1
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/conftest.py +15 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/integration/test_send_verified_tmux.py +194 -5
- agentwire_dev-1.67.0/tests/integration/test_tmux_name_rewrite.py +166 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/integration/test_worktree_cmd.py +467 -6
- agentwire_dev-1.67.0/tests/unit/test_build_agent_command.py +358 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_cli_commands.py +364 -32
- agentwire_dev-1.67.0/tests/unit/test_cohort.py +345 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_doctor_scheduler_staleness.py +58 -15
- agentwire_dev-1.67.0/tests/unit/test_ensure_shared_dir.py +135 -0
- agentwire_dev-1.67.0/tests/unit/test_ensure_verified_prompt.py +155 -0
- agentwire_dev-1.67.0/tests/unit/test_helper_session.py +225 -0
- agentwire_dev-1.67.0/tests/unit/test_history_resume_name.py +126 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_idle_handler.py +79 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_inbox.py +101 -4
- agentwire_dev-1.67.0/tests/unit/test_launch_via_env.py +106 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_mcp_server.py +11 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_mcp_tools_args.py +15 -0
- agentwire_dev-1.67.0/tests/unit/test_pending_message_visibility.py +149 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_portal_api.py +28 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_project_config.py +99 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_prompt_router.py +38 -6
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_scheduler.py +2 -2
- agentwire_dev-1.67.0/tests/unit/test_scheduler_liveness.py +351 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_scheduler_zombie.py +73 -1
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_session_ready.py +467 -42
- agentwire_dev-1.67.0/tests/unit/test_task_max_duration.py +171 -0
- agentwire_dev-1.67.0/tests/unit/test_worktree_dot_project.py +146 -0
- agentwire_dev-1.67.0/tests/unit/test_worktree_git_truth.py +314 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_worktree_layout.py +29 -12
- agentwire_dev-1.65.3/tests/unit/test_build_agent_command.py +0 -163
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/.agentwire.yml.example +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/.claude/commands/handoff.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/.claude/skills/wiki/SKILL.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/.github/FUNDING.yml +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/.github/ISSUE_TEMPLATE/question.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/.github/workflows/pytest.yml +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/.github/workflows/ruff.yml +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/.github/workflows/security.yml +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/.github/workflows/tts-smoke.yml +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/.gitignore +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/AGENTS.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/CODE_OF_CONDUCT.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/CONTRIBUTING.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/Dockerfile.local +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/LICENSE +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/README.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/RELEASING.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/SECURITY.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/SPONSORS.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/agents/__init__.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/agents/base.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/agents/tmux.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/cached_status.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/channels/__init__.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/channels/base.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/channels/email.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/channels/push.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/channels/quo.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/channels_cli.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/chrome_tabs.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/config.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/config_cli.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/council/__init__.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/council/cli.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/council/inbox.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/council/minutes.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/council/state.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/council/view.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/devices.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/diff_cli.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/doctor_voice.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/fetch.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/handoff/__init__.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/handoff/git_state.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/handoff/instructions.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/handoff/parser.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/handoff/renderer.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/handoff/schema.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/handoff_cli.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/history.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/hooks/__init__.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/hooks/agentwire-permission.sh +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/hooks/damage-control/__init__.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/hooks/damage-control/audit_logger.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/hooks/damage-control/bash-tool-damage-control.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/hooks/damage-control/edit-tool-damage-control.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/hooks/damage-control/mcp-tool-damage-control.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/hooks/damage-control/read-tool-damage-control.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/hooks/damage-control/rules/agentwire.yaml +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/hooks/damage-control/rules/aws.yaml +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/hooks/damage-control/rules/cloud-hosting.yaml +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/hooks/damage-control/rules/containers.yaml +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/hooks/damage-control/rules/control-plane.yaml +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/hooks/damage-control/rules/core.yaml +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/hooks/damage-control/rules/databases.yaml +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/hooks/damage-control/rules/firebase.yaml +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/hooks/damage-control/rules/gcp.yaml +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/hooks/damage-control/rules/git.yaml +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/hooks/damage-control/rules/gws.yaml +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/hooks/damage-control/rules/infrastructure.yaml +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/hooks/damage-control/rules/outbound.yaml +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/hooks/damage-control/rules/publish.yaml +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/hooks/damage-control/rules/remote.yaml +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/hooks/damage-control/write-tool-damage-control.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/hooks/queue-processor.sh +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/hooks_cli.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/listen.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/locking.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/machine_cli.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/mcp_channels.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/mcp_chrome.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/mcp_cli.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/mcp_council.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/mcp_desktop.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/mcp_handoff.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/mcp_history.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/mcp_listen.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/mcp_lock.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/mcp_machine.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/mcp_msg.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/mcp_notify.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/mcp_pane.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/mcp_projects.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/mcp_scheduler.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/mcp_scratchpad.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/mcp_server.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/mcp_services.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/mcp_status.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/mcp_tunnels.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/mcp_voice.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/mcp_wiki.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/msg_cli.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/network.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/notify_cli.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/onboarding.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/palette_cli.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/portal_cli.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/projects.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/prompt_router.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/prompts_cli.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/push_cli.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/push_store.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/repo_cli.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/research.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/roles/__init__.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/roles/agentwire.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/roles/anchor.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/roles/chatbot.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/roles/contributor.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/roles/correspondent.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/roles/council-brain.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/roles/council-conscience.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/roles/council-critic.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/roles/council-devils-advocate.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/roles/council-gut.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/roles/council-historian.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/roles/council-member.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/roles/council-orchestrator.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/roles/init.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/roles/notifications.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/roles/orchestrator.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/roles/reviewer.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/roles/soul.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/roles/task-runner.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/roles/voice.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/roles/worker.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/roles_cli.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/routes/__init__.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/routes/artifacts.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/routes/config.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/routes/council.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/routes/desktop.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/routes/history.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/routes/machines.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/routes/notify.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/routes/palette.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/routes/permission.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/routes/projects.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/routes/push.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/routes/safety.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/routes/scratchpad.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/routes/sessions.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/routes/sessions_admin.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/routes/voice.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/safety/__init__.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/safety/_core.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/safety_cli.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/safety_commands.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/scheduler/gates.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/scheduler/loop.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/scheduler/models.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/scheduler/schedule.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/scheduler/state.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/scratchpad.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/security.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/services.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/session_context.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/ssh.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/agentwire-splash-logo-layers--agentwire-text.png +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/agentwire-splash-logo-layers--echo-claw-fg.png +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/agentwire-splash-logo-layers--echo.png +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/agentwire-splash-logo-layers--telephone.png +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/agentwire-splash-logo-layers--tree.png +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/announcements.json +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/apple-touch-icon.png +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/css/desktop.css +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/css/mobile.css +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/favicon-32.png +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/favicon-64.png +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/icons/sessions/bear.webp +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/icons/sessions/cat.webp +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/icons/sessions/crown.webp +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/icons/sessions/custom/agentwire-portal.webp +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/icons/sessions/custom/agentwire-tts.webp +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/icons/sessions/custom/agentwire.webp +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/icons/sessions/deer.webp +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/icons/sessions/drone.webp +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/icons/sessions/eagle.webp +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/icons/sessions/fox.webp +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/icons/sessions/hawk.webp +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/icons/sessions/horse.webp +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/icons/sessions/lion.webp +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/icons/sessions/rabbit.webp +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/icons/sessions/robot.webp +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/icons/sessions/tiger.webp +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/icons/sessions/wolf.webp +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/img/icon-192.png +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/img/icon-512.png +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/.gitkeep +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/announcement-modal.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/api.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/artifact-window.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/card-terminal.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/collage.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/command-palette.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/council-window.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/dead-key-suppressor.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/desktop-manager.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/desktop.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/help-modal.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/icon-manager.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/lineage.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/mobile.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/notification-prefs.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/notifications-panel.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/ptt.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/push-client.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/review-window.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/safety-shared.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/safety-window.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/scratchpad.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/service-classification.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/session-hud-controller.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/session-hud-notices.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/session-hud-spawn.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/session-hud.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/session-id.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/session-window.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/shortcuts.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/sidebar/artifacts-section.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/sidebar/config-section.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/sidebar/council-section.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/sidebar/machines-section.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/sidebar/projects-section.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/sidebar/safety-section.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/sidebar/scheduler-section.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/sidebar/services-section.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/sidebar/sessions-section.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/sidebar.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/spawn-ghost.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/terminal-font-prefs.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/terminal-pane.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/tile-manager.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/toast.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/token-modal.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/topology-render.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/utils/ansi.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/utils/rich-text.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/utils/swipe.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/voice/autosend-prefs.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/voice/browser-stt.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/voice/browser-tts.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/voice/jargon.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/voice/prompt.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/winbox.bundle.min.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/js/workspace-window.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/manifest.webmanifest +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/service-worker.js +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/static/topology-harness.html +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/stt/__init__.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/stt/base.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/stt/cloud.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/stt/engine.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/stt/local.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/stt/server_backend.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/stt/stt_server.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/tabs_cli.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/templates/__init__.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/templates/base.html +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/templates/council/minutes.html.j2 +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/templates/desktop.html +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/templates/email_notification.html +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/templates/handoff/show-the-story.html.j2 +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/templates/handoff/theme.css.j2 +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/templates/mobile.html +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/templates/pair.html +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/templating.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/tooldefs/aws.yaml +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/tooldefs/docker.yaml +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/tooldefs/gcp.yaml +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/tooldefs/gh.yaml +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/tooldefs/git.yaml +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/tooldefs/gws.yaml +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/tooldefs/kubectl.yaml +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/tooldefs/npm.yaml +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/tooldefs/terraform.yaml +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/tooldefs/uv.yaml +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/tts/__init__.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/tts/audio.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/tts/base.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/tts/engines/__init__.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/tts/engines/chatterbox.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/tts/engines/kokoro.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/tts/engines/zonos.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/tts/kokoro_server.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/tts/local.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/tts/registry.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/tts_cli.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/tts_server.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/tunnels.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/tunnels_cli.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/usage_limit.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/utils/__init__.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/utils/chunker.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/utils/event_log.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/utils/file_io.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/utils/paths.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/utils/speech.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/utils/subprocess.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/validation.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/voice_status.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/wiki.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/wiki_audit.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/agentwire/wiki_cli.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/decisions/obsidian-second-brain.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/design/session-card-fields.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/design/session-card-recommendation.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/logo.png +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/architecture.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/briefing-mode.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/communication/channels.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/communication/hammerspoon.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/communication/handoff.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/concepts.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/council.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/deployment/remote-access.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/deployment/remote-machines.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/glossary.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/integrations/gws-google-workspace-cli.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/internals/damage-control.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/internals/parallel-refactor.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/internals/portal.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/internals/session-topology.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/internals/troubleshooting.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/internals/window-collage.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/research/briefing-mode-feasibility.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/research/orchestration-transport-alternatives.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/security/damage-control-hardening.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/security/pip-audit.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/security/remote-access-hardening.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/security/secrets.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/services.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/sessions/claude-code-auto-mode.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/sessions/prompt-routing.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/sessions/window-sizing.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/usage-limit-recovery.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/voice/shim-contract.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/voice/stt-cloud.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/voice/stt-self-hosted.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/docs/wiki/voice/tts-self-hosted.md +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/e2e/test_portal_ui.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/fixtures/sample_agentwire.yml +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/fixtures/sample_config.yaml +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/fixtures/sample_scheduler.yaml +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/integration/test_council_portal.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/integration/test_scheduler_board.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/integration/test_server_websockets.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_announcements.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_channels.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_chrome_tabs.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_cli_output.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_cli_smoke.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_config.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_config_cli.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_control_plane_protection.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_core_created_by_rooting.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_council_cli.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_council_inbox.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_council_minutes.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_council_state.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_council_view.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_damage_control_bypass.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_damage_control_hooks.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_damage_control_sync.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_dangling_worktrees.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_devices.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_diff_cli.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_doctor_task_migration.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_doctor_voice.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_event_log.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_file_io.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_guarded_launch_command.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_handoff_git_state.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_handoff_instructions.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_handoff_parser.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_handoff_renderer.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_history.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_hooks_install.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_idle_nag.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_init_command.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_install_parity.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_limits_tick_isolation.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_locking.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_msg_cli.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_notify_cli.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_onboarding_reinit.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_palette_cli.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_pip_environment.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_posture_default.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_projects_registry.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_push.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_rebuild_guard.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_repo_cli.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_rich_text_js.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_roles.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_routes_snapshot.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_safety_commands.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_safety_disabled_rules.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_safety_escape_hatch.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_safety_heal.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_safety_kill_switch.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_scheduler_parsing.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_scratchpad.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_security.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_server_async.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_server_pure.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_services.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_session_context.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_session_created_by_env.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_shared_dir_guard.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_shim_selfheal.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_skill_install.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_speech_tags.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_ssh.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_stt_backend.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_stt_cloud.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_stt_engine.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_task_cli.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_tasks.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_tasks_cli.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_templating.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_terminal_resize.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_tmux_template.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_tts_engine_resolution.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_tts_local.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_tunnels_probe.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_unattended_env_isolation.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_usage_limit.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_validation.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_voice_status.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_wiki.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_wiki_audit.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_window_cycling_binding.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_worktree.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_worktree_branch_cleanup.py +0 -0
- {agentwire_dev-1.65.3 → agentwire_dev-1.67.0}/tests/unit/test_worktree_registry.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentwire-dev
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.67.0
|
|
4
4
|
Summary: Multi-session voice web interface for AI coding agents
|
|
5
5
|
Project-URL: Homepage, https://agentwire.dev
|
|
6
6
|
Project-URL: Repository, https://github.com/dotdevdotdev/agentwire-dev
|
|
@@ -231,7 +231,7 @@ Requires-Dist: fastapi>=0.104.0
|
|
|
231
231
|
Requires-Dist: jinja2>=3.1.0
|
|
232
232
|
Requires-Dist: kokoro-onnx>=0.4.9; python_version < '3.14'
|
|
233
233
|
Requires-Dist: markdown>=3.5
|
|
234
|
-
Requires-Dist: mcp
|
|
234
|
+
Requires-Dist: mcp<2,>=1.2.0
|
|
235
235
|
Requires-Dist: pydantic>=2.0.0
|
|
236
236
|
Requires-Dist: python-dotenv>=1.2.2
|
|
237
237
|
Requires-Dist: python-multipart>=0.0.6
|
|
@@ -37,7 +37,6 @@ from .core import ( # noqa: E402,F401 # E402: must follow load_dotenv(); F401:
|
|
|
37
37
|
_parse_session_target,
|
|
38
38
|
_portal_auth_headers,
|
|
39
39
|
_post_desktop_notification,
|
|
40
|
-
_record_session_creator,
|
|
41
40
|
_resolve_posture_from_args,
|
|
42
41
|
_run_remote,
|
|
43
42
|
_set_session_name_env,
|
|
@@ -58,6 +57,7 @@ from .core import ( # noqa: E402,F401 # E402: must follow load_dotenv(); F401:
|
|
|
58
57
|
load_config,
|
|
59
58
|
load_session_metadata,
|
|
60
59
|
parse_env_args,
|
|
60
|
+
record_session_launch,
|
|
61
61
|
store_session_metadata,
|
|
62
62
|
tmux_session_exists,
|
|
63
63
|
tmux_session_has_agent,
|
|
@@ -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()
|
|
@@ -187,25 +187,38 @@ def wait_for_completion_signal(
|
|
|
187
187
|
session: str,
|
|
188
188
|
poll_interval: float = 10.0,
|
|
189
189
|
summary_path: Path | None = None,
|
|
190
|
+
max_duration: int = 0,
|
|
190
191
|
) -> dict:
|
|
191
192
|
"""Wait for task completion by polling the summary file directly.
|
|
192
193
|
|
|
193
194
|
Exits when:
|
|
194
195
|
1. Summary file appears (task completed normally)
|
|
195
196
|
2. Session dies (agent crashed, tmux killed)
|
|
197
|
+
3. ``max_duration`` elapses, when the task sets one
|
|
196
198
|
|
|
197
|
-
|
|
199
|
+
Completion is otherwise agent-driven: the idle hook fires, the agent writes
|
|
200
|
+
a summary, and this returns. An agent that never goes idle never produces
|
|
201
|
+
either exit — wedged on an unrecognized dialog, or blocked inside a tool
|
|
202
|
+
call — and before ``max_duration`` existed the wait was unbounded, so that
|
|
203
|
+
read as a silent multi-hour hang whose only visible end was the scheduler's
|
|
204
|
+
4h process-group watchdog (#867).
|
|
205
|
+
|
|
206
|
+
``max_duration`` is a per-attempt wall clock, not an idle timer: it can't
|
|
207
|
+
tell a wedged agent from a slow one, so a task that legitimately runs long
|
|
208
|
+
should set it high or leave it at 0 rather than get killed mid-work.
|
|
198
209
|
|
|
199
210
|
Args:
|
|
200
211
|
session: tmux session name
|
|
201
212
|
poll_interval: Seconds between checks
|
|
202
213
|
summary_path: Path to the summary .md file the agent will write
|
|
214
|
+
max_duration: Seconds before giving up (0 = unbounded)
|
|
203
215
|
|
|
204
216
|
Returns:
|
|
205
217
|
Dict with 'status', 'summary', 'summary_file' keys
|
|
206
218
|
|
|
207
219
|
Raises:
|
|
208
|
-
CompletionTimeout: If session dies
|
|
220
|
+
CompletionTimeout: If the session dies, or max_duration elapses, before
|
|
221
|
+
the task completed. The message names which.
|
|
209
222
|
"""
|
|
210
223
|
# Build glob pattern for fuzzy summary detection (agents sometimes
|
|
211
224
|
# invent their own timestamp instead of using the provided filename).
|
|
@@ -217,6 +230,8 @@ def wait_for_completion_signal(
|
|
|
217
230
|
prefix = stem[:-19] if len(stem) > 19 else stem
|
|
218
231
|
summary_glob = f"{prefix}*.md"
|
|
219
232
|
|
|
233
|
+
started = time.time()
|
|
234
|
+
|
|
220
235
|
while True:
|
|
221
236
|
# Primary: check if the agent has written the summary file AND
|
|
222
237
|
# the hook has deleted the context file (signals cleanup complete).
|
|
@@ -274,6 +289,14 @@ def wait_for_completion_signal(
|
|
|
274
289
|
f"Session '{session}' died or agent exited before task completed"
|
|
275
290
|
)
|
|
276
291
|
|
|
292
|
+
if max_duration > 0:
|
|
293
|
+
elapsed = time.time() - started
|
|
294
|
+
if elapsed >= max_duration:
|
|
295
|
+
raise CompletionTimeout(
|
|
296
|
+
f"Task exceeded max_duration ({max_duration}s) after "
|
|
297
|
+
f"{int(elapsed)}s — the agent never signalled completion"
|
|
298
|
+
)
|
|
299
|
+
|
|
277
300
|
time.sleep(poll_interval)
|
|
278
301
|
|
|
279
302
|
|