agentwire-dev 1.2.0__tar.gz → 1.3.1__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.2.0 → agentwire_dev-1.3.1}/CHANGELOG.md +21 -1
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/PKG-INFO +1 -1
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/__init__.py +1 -1
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/__main__.py +88 -58
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/completion.py +71 -40
- agentwire_dev-1.2.0/agentwire/hooks/suppress-bg-notifications.sh → agentwire_dev-1.3.1/agentwire/hooks/idle-handler.sh +14 -8
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/locking.py +9 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/mcp_server.py +20 -1
- agentwire_dev-1.3.1/agentwire/onboarding.py +435 -0
- agentwire_dev-1.3.1/agentwire/roles/init.md +194 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/server.py +65 -42
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/css/desktop.css +58 -12
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/js/desktop-manager.js +31 -13
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/js/desktop.js +103 -18
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/js/list-window.js +41 -50
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/js/session-window.js +20 -79
- agentwire_dev-1.3.1/agentwire/static/js/tile-manager.js +346 -0
- agentwire_dev-1.3.1/agentwire/stt/__init__.py +58 -0
- agentwire_dev-1.3.1/agentwire/stt/server_backend.py +88 -0
- agentwire_dev-1.3.1/agentwire/utils/chunker.py +27 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/TROUBLESHOOTING.md +1 -1
- agentwire_dev-1.3.1/docs/brainstorms/idea-agent-hot-swap.md +376 -0
- agentwire_dev-1.3.1/docs/brainstorms/idea-checkpoint-commits.md +345 -0
- agentwire_dev-1.3.1/docs/brainstorms/idea-context-window-gauge.md +360 -0
- agentwire_dev-1.3.1/docs/brainstorms/idea-cross-session-events.md +244 -0
- agentwire_dev-1.3.1/docs/brainstorms/idea-delegation-replay.md +152 -0
- agentwire_dev-1.3.1/docs/brainstorms/idea-device-session-tethering.md +88 -0
- agentwire_dev-1.3.1/docs/brainstorms/idea-periodic-voice-briefings.md +257 -0
- agentwire_dev-1.3.1/docs/brainstorms/idea-session-drift-detection.md +359 -0
- agentwire_dev-1.3.1/docs/brainstorms/idea-session-energy-model.md +407 -0
- agentwire_dev-1.3.1/docs/brainstorms/idea-session-momentum.md +319 -0
- agentwire_dev-1.3.1/docs/brainstorms/idea-session-replay.md +173 -0
- agentwire_dev-1.3.1/docs/brainstorms/idea-session-thermal-throttling.md +138 -0
- agentwire_dev-1.3.1/docs/brainstorms/idea-spatial-voice-mixing.md +153 -0
- agentwire_dev-1.3.1/docs/brainstorms/idea-speculative-execution.md +408 -0
- agentwire_dev-1.3.1/docs/brainstorms/idea-task-time-budgets.md +120 -0
- agentwire_dev-1.3.1/docs/brainstorms/idea-voice-activity-zones.md +487 -0
- agentwire_dev-1.3.1/docs/brainstorms/idea-voice-breakpoints.md +179 -0
- agentwire_dev-1.3.1/docs/brainstorms/idea-voice-code-review.md +246 -0
- agentwire_dev-1.3.1/docs/brainstorms/idea-voice-command-undo.md +468 -0
- agentwire_dev-1.3.1/docs/brainstorms/idea-worker-fencing.md +370 -0
- agentwire_dev-1.3.1/docs/brainstorms/idea-worker-proof-of-work.md +296 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/notification-hooks.md +1 -1
- agentwire_dev-1.2.0/agentwire/onboarding.py +0 -1580
- agentwire_dev-1.2.0/agentwire/stt/__init__.py +0 -41
- agentwire_dev-1.2.0/docs/brainstorms/idea-session-replay.md +0 -247
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/.github/FUNDING.yml +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/.github/ISSUE_TEMPLATE/question.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/.github/workflows/ci.yml +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/.github/workflows/publish.yml +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/.gitignore +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/CLA.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/CODE_OF_CONDUCT.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/CONTRIBUTING.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/Dockerfile.local +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/Dockerfile.runpod +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/LICENSE +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/README.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/RELEASING.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/SECURITY.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/SPONSORS.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/agents/__init__.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/agents/base.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/agents/tmux.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/cached_status.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/cli_safety.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/config.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/errors.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/history.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/hooks/__init__.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/hooks/agentwire-permission.sh +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/hooks/damage-control/__init__.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/hooks/damage-control/audit_logger.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/hooks/damage-control/bash-tool-damage-control.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/hooks/damage-control/edit-tool-damage-control.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/hooks/damage-control/patterns.yaml +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/hooks/damage-control/write-tool-damage-control.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/init_agentwire.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/listen.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/network.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/notifications.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/pane_manager.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/project_config.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/projects.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/prompts/__init__.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/prompts/init.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/roles/__init__.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/roles/chatbot.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/roles/claude-delegation.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/roles/claude-worker-haiku.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/roles/claude-worker-sonnet.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/roles/claude-worker.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/roles/glm-delegation.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/roles/glm-worker-flash.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/roles/glm-worker.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/roles/leader-claude.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/roles/leader-glm.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/roles/leader-openai.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/roles/leader.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/roles/openai-delegation.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/roles/openai-worker-mini.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/roles/openai-worker.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/roles/task-runner.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/roles/voice.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/roles/worker.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/agentwire-Echo--black.png +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/agentwire-Echo--transparent.png +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/agentwire-Echo.png +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/agentwire-email-banner.png +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/agentwire-splash-logo-layers--agentwire-text.png +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/agentwire-splash-logo-layers--echo-claw-fg.png +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/agentwire-splash-logo-layers--echo.png +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/agentwire-splash-logo-layers--full--transparent-top.png +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/agentwire-splash-logo-layers--full-black.png +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/agentwire-splash-logo-layers--telephone-fg.png +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/agentwire-splash-logo-layers--telephone.png +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/agentwire-splash-logo-layers--transparent-top.png +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/agentwire-splash-logo-layers--tree.png +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/agentwire-splash-logo-layers.png +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/favicon.png +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/machines/android.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/machines/automaton.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/machines/bot.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/machines/cyborg.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/machines/droid.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/machines/drone.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/machines/guardian.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/machines/mech.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/machines/probe.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/machines/robot.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/machines/sentinel.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/machines/unit.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/projects/blob.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/projects/cloud.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/projects/crystal.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/projects/cyclops.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/projects/flame.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/projects/fuzzy.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/projects/horned.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/projects/moon.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/projects/slime.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/projects/star.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/projects/tentacle.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/projects/winged.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/sessions/bear.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/sessions/cat.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/sessions/crown.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/sessions/custom/agentwire-portal.png +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/sessions/custom/agentwire-tts.png +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/sessions/custom/agentwire.png +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/sessions/deer.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/sessions/drone.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/sessions/eagle.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/sessions/fox.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/sessions/hawk.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/sessions/horse.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/sessions/lion.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/sessions/rabbit.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/sessions/robot.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/sessions/tiger.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/icons/sessions/wolf.jpeg +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/js/.gitkeep +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/js/components/icon-picker.js +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/js/components/list-card.js +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/js/components/type-tag.js +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/js/icon-manager.js +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/js/utils/auto-refresh.js +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/js/winbox.bundle.min.js +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/js/windows/chat-window.js +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/js/windows/config-window.js +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/js/windows/machines-window.js +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/js/windows/projects-window.js +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/static/js/windows/sessions-window.js +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/stt/base.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/stt/stt_server.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/stt/whisperkit.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/tasks.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/templates/__init__.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/templates/base.html +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/templates/desktop.html +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/templates/email_notification.html +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/templating.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/tts/__init__.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/tts/base.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/tts/engines/__init__.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/tts/engines/chatterbox.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/tts/engines/qwen_base.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/tts/engines/qwen_custom.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/tts/engines/qwen_design.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/tts/registry.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/tts/runpod_handler.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/tts_server.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/tunnels.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/utils/__init__.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/utils/file_io.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/utils/paths.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/utils/subprocess.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/validation.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/voiceclone.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/voices/darren.wav +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/voices/default.wav +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/voices/jessica.wav +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/voices/lisa.wav +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/voices/may.wav +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/agentwire/worktree.py +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/PORTAL.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/SHELL_ESCAPING.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/SPONSORS.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/brainstorms/README.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/brainstorms/idea-ambient-context-stream.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/brainstorms/idea-ambient-listening-mode.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/brainstorms/idea-audio-cues.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/brainstorms/idea-context-compression-protocol.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/brainstorms/idea-contextual-bookmarks.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/brainstorms/idea-conversation-archaeology.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/brainstorms/idea-cost-tracking-dashboard.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/brainstorms/idea-failure-memory.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/brainstorms/idea-notification-escalation.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/brainstorms/idea-presence-aware-sessions.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/brainstorms/idea-session-handshake.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/brainstorms/idea-session-snapshots.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/brainstorms/idea-session-templates.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/brainstorms/idea-smart-session-routing.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/brainstorms/idea-task-pipeline-chaining.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/brainstorms/idea-task-pivot-protocol.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/brainstorms/idea-voice-handoff.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/brainstorms/idea-voice-identity.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/brainstorms/idea-voice-interrupts-v2.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/brainstorms/idea-voice-interrupts.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/brainstorms/idea-voice-macros.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/brainstorms/idea-voice-transcript-logs.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/brainstorms/idea-watchdog-mode.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/brainstorms/idea-worker-file-coordination.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/brainstorms/idea-worker-health-dashboard.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/brainstorms/idea-worker-heartbeat-watchdog.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/brainstorms/idea-worker-progress-streaming.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/brainstorms/idea-worker-warmup.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/brainstorms/idea-x-api-integration.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/critical-analysis.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/demo-script.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/issues/pending-code-changes.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/issues/remote-tts-session-detection.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/logo.png +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/progressive-loading-pattern.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/prompts/agentwire-website.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/releasing.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/remote-access.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/remote-machines.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/runpod-tts.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/scheduled-workloads.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/security/damage-control-migration.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/security/damage-control.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/tmux-hooks.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/tts-self-hosted.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/docs/youtube-channel.md +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/opencode-plugin/agentwire-notify.ts +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/pyproject.toml +0 -0
- {agentwire_dev-1.2.0 → agentwire_dev-1.3.1}/requirements-tts.txt +0 -0
|
@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.3.0] - 2026-02-10
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Drag-to-tile window management for side-by-side session workflows
|
|
15
|
+
- Auto-chunk long TTS messages into separate audio segments for sequential playback
|
|
16
|
+
- Redesigned onboarding flow that asks 3 questions then spawns Claude for setup
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- Chunk pasted terminal input to prevent PTY buffer flooding and session freezes
|
|
21
|
+
- Poll summary file directly instead of relying on two-idle completion signal
|
|
22
|
+
- Move TTS chunker to utils to avoid torch import in MCP server
|
|
23
|
+
- Namespace task summary files by session to prevent cross-session collisions
|
|
24
|
+
- Don't clear task context on ensure timeout (race condition)
|
|
25
|
+
- Stale lock detection in `--wait-lock` + add `--skip-if-locked`
|
|
26
|
+
- Use STT server when configured instead of always falling back to WhisperKit
|
|
27
|
+
|
|
10
28
|
## [1.2.0] - 2026-02-03
|
|
11
29
|
|
|
12
30
|
### Added
|
|
@@ -112,4 +130,6 @@ Initial public release of AgentWire.
|
|
|
112
130
|
[1.0.0]: https://github.com/dotdevdotdev/agentwire-dev/releases/tag/v1.0.0
|
|
113
131
|
|
|
114
132
|
[1.1.0]: https://github.com/dotdevdotdev/agentwire-dev/compare/v1.0.0...v1.1.0
|
|
115
|
-
[
|
|
133
|
+
[1.2.0]: https://github.com/dotdevdotdev/agentwire-dev/compare/v1.1.0...v1.2.0
|
|
134
|
+
[1.3.0]: https://github.com/dotdevdotdev/agentwire-dev/compare/v1.2.0...v1.3.0
|
|
135
|
+
[Unreleased]: https://github.com/dotdevdotdev/agentwire-dev/compare/v1.3.0...HEAD
|
|
@@ -331,6 +331,32 @@ def tmux_session_exists(name: str) -> bool:
|
|
|
331
331
|
return result.returncode == 0
|
|
332
332
|
|
|
333
333
|
|
|
334
|
+
def _get_session_project_path(session: str) -> Path | None:
|
|
335
|
+
"""Get a session's project path from its tmux working directory.
|
|
336
|
+
|
|
337
|
+
Queries tmux for the session's actual working directory. Falls back to
|
|
338
|
+
deriving it from the session name if the session isn't running.
|
|
339
|
+
|
|
340
|
+
Returns:
|
|
341
|
+
Path to the project directory, or None if not determinable.
|
|
342
|
+
"""
|
|
343
|
+
# Try to get the actual working directory from tmux
|
|
344
|
+
if tmux_session_exists(session):
|
|
345
|
+
result = subprocess.run(
|
|
346
|
+
["tmux", "display-message", "-t", session, "-p", "#{pane_current_path}"],
|
|
347
|
+
capture_output=True,
|
|
348
|
+
text=True,
|
|
349
|
+
)
|
|
350
|
+
if result.returncode == 0 and result.stdout.strip():
|
|
351
|
+
return Path(result.stdout.strip())
|
|
352
|
+
|
|
353
|
+
# Fallback: derive from session name
|
|
354
|
+
config = load_config()
|
|
355
|
+
projects_dir = Path(config.get("projects", {}).get("dir", "~/projects")).expanduser()
|
|
356
|
+
project, _, _ = parse_session_name(session)
|
|
357
|
+
return projects_dir / project
|
|
358
|
+
|
|
359
|
+
|
|
334
360
|
def tmux_session_has_agent(name: str) -> bool:
|
|
335
361
|
"""Check if a tmux session has an agent running (not just a bare shell).
|
|
336
362
|
|
|
@@ -991,11 +1017,11 @@ def _start_tts_local(args, venv_override: str | None = None) -> int:
|
|
|
991
1017
|
print(f"Create it with: cd {source_dir} && uv venv {venv_name}", file=sys.stderr)
|
|
992
1018
|
return 1
|
|
993
1019
|
|
|
994
|
-
# Build command
|
|
1020
|
+
# Build command using venv python directly (avoids broken activate scripts and conda interference)
|
|
1021
|
+
venv_python = source_dir / venv_name / "bin" / "python"
|
|
995
1022
|
tts_cmd = (
|
|
996
1023
|
f"cd {source_dir} && "
|
|
997
|
-
f"
|
|
998
|
-
f"python -m agentwire tts serve --host {host} --port {port} --backend {backend} --venv {venv}"
|
|
1024
|
+
f"{venv_python} -m agentwire tts serve --host {host} --port {port} --backend {backend} --venv {venv}"
|
|
999
1025
|
)
|
|
1000
1026
|
|
|
1001
1027
|
print(f"Starting TTS server on {host}:{port} (backend: {backend}, venv: {venv})...")
|
|
@@ -1991,19 +2017,27 @@ def cmd_say(args) -> int:
|
|
|
1991
2017
|
_handle_voice_notifications(text, voice, args, session)
|
|
1992
2018
|
|
|
1993
2019
|
# Try portal first if we have a session
|
|
2020
|
+
# Portal handles chunking internally (sequential generation + broadcast)
|
|
1994
2021
|
if session:
|
|
1995
2022
|
portal_url = _get_portal_url()
|
|
1996
2023
|
has_connections, actual_session = _check_portal_connections(session, portal_url)
|
|
1997
2024
|
|
|
1998
2025
|
if has_connections:
|
|
1999
|
-
# Send to portal - browser will play the audio
|
|
2000
2026
|
return _remote_say(text, actual_session, portal_url)
|
|
2001
2027
|
|
|
2002
|
-
# No portal connections
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2028
|
+
# No portal connections — chunk locally for better TTS quality
|
|
2029
|
+
from .utils.chunker import chunk_text
|
|
2030
|
+
chunks = chunk_text(text)
|
|
2031
|
+
|
|
2032
|
+
for chunk in chunks:
|
|
2033
|
+
result = _local_say_runpod(
|
|
2034
|
+
chunk, voice, exaggeration, cfg_weight, tts_config,
|
|
2035
|
+
backend=backend, instruct=instruct, language=language, stream=stream
|
|
2036
|
+
)
|
|
2037
|
+
if result != 0:
|
|
2038
|
+
return result
|
|
2039
|
+
|
|
2040
|
+
return 0
|
|
2007
2041
|
|
|
2008
2042
|
|
|
2009
2043
|
def cmd_alert(args) -> int:
|
|
@@ -5074,7 +5108,7 @@ def cmd_init(args) -> int:
|
|
|
5074
5108
|
# Quick mode: run wizard but skip agentwire step
|
|
5075
5109
|
# We do this by running onboarding and returning before agentwire prompt
|
|
5076
5110
|
# The onboarding module handles this internally
|
|
5077
|
-
return run_onboarding(
|
|
5111
|
+
return run_onboarding(skip_session=True)
|
|
5078
5112
|
|
|
5079
5113
|
# Default: run full wizard (ends with optional agentwire setup)
|
|
5080
5114
|
return run_onboarding()
|
|
@@ -5408,7 +5442,7 @@ def cmd_doctor(args) -> int:
|
|
|
5408
5442
|
print(" Run: agentwire hooks install")
|
|
5409
5443
|
|
|
5410
5444
|
# Check Claude Code idle notification hook
|
|
5411
|
-
idle_hook = CLAUDE_HOOKS_DIR / "
|
|
5445
|
+
idle_hook = CLAUDE_HOOKS_DIR / "idle-handler.sh"
|
|
5412
5446
|
if idle_hook.exists():
|
|
5413
5447
|
print(f" [ok] Idle notification hook: {idle_hook}")
|
|
5414
5448
|
else:
|
|
@@ -6659,7 +6693,6 @@ def cmd_ensure(args) -> int:
|
|
|
6659
6693
|
CompletionTimeout,
|
|
6660
6694
|
generate_summary_filename,
|
|
6661
6695
|
get_summary_prompt,
|
|
6662
|
-
parse_summary_file,
|
|
6663
6696
|
status_to_exit_code,
|
|
6664
6697
|
wait_for_file,
|
|
6665
6698
|
wait_for_idle,
|
|
@@ -6682,6 +6715,7 @@ def cmd_ensure(args) -> int:
|
|
|
6682
6715
|
dry_run = getattr(args, 'dry_run', False)
|
|
6683
6716
|
wait_lock = getattr(args, 'wait_lock', False)
|
|
6684
6717
|
lock_timeout = getattr(args, 'lock_timeout', 60)
|
|
6718
|
+
skip_if_locked = getattr(args, 'skip_if_locked', False)
|
|
6685
6719
|
json_mode = getattr(args, 'json', False)
|
|
6686
6720
|
|
|
6687
6721
|
# Parse session target
|
|
@@ -6690,14 +6724,11 @@ def cmd_ensure(args) -> int:
|
|
|
6690
6724
|
if machine_id:
|
|
6691
6725
|
return _output_result(False, json_mode, "Remote sessions not yet supported for ensure", exit_code=ENSURE_EXIT_SESSION_ERROR)
|
|
6692
6726
|
|
|
6693
|
-
# Find project path from --project flag, or
|
|
6727
|
+
# Find project path from --project flag, or session's working directory
|
|
6694
6728
|
if hasattr(args, 'project') and args.project:
|
|
6695
6729
|
project_path = Path(args.project).expanduser().resolve()
|
|
6696
6730
|
else:
|
|
6697
|
-
|
|
6698
|
-
projects_dir = Path(config.get("projects", {}).get("dir", "~/projects")).expanduser()
|
|
6699
|
-
project, branch, _ = parse_session_name(session_name)
|
|
6700
|
-
project_path = projects_dir / project
|
|
6731
|
+
project_path = _get_session_project_path(session)
|
|
6701
6732
|
|
|
6702
6733
|
if not project_path.exists():
|
|
6703
6734
|
return _output_result(False, json_mode, f"Project path not found: {project_path}", exit_code=ENSURE_EXIT_SESSION_ERROR)
|
|
@@ -6776,8 +6807,12 @@ def cmd_ensure(args) -> int:
|
|
|
6776
6807
|
args, session, task, ctx, shell, project_path, timeout, json_mode
|
|
6777
6808
|
)
|
|
6778
6809
|
except LockConflict as e:
|
|
6810
|
+
if skip_if_locked:
|
|
6811
|
+
return 0
|
|
6779
6812
|
return _output_result(False, json_mode, str(e), exit_code=ENSURE_EXIT_LOCK_CONFLICT)
|
|
6780
6813
|
except LockTimeout as e:
|
|
6814
|
+
if skip_if_locked:
|
|
6815
|
+
return 0
|
|
6781
6816
|
return _output_result(False, json_mode, str(e), exit_code=ENSURE_EXIT_LOCK_CONFLICT)
|
|
6782
6817
|
|
|
6783
6818
|
|
|
@@ -6787,15 +6822,14 @@ def _run_ensure_task(args, session, task, ctx, shell, project_path, timeout, jso
|
|
|
6787
6822
|
Uses hook-based completion detection:
|
|
6788
6823
|
1. Write task context file (tells hook a scheduled task is running)
|
|
6789
6824
|
2. Send task prompt
|
|
6790
|
-
3. Hook handles: first idle → send summary prompt
|
|
6791
|
-
4.
|
|
6792
|
-
5.
|
|
6825
|
+
3. Hook handles: first idle → send summary prompt
|
|
6826
|
+
4. Poll for summary file (agent writes it after receiving summary prompt)
|
|
6827
|
+
5. Parse summary and return result
|
|
6793
6828
|
"""
|
|
6794
6829
|
from .completion import (
|
|
6795
6830
|
CompletionTimeout,
|
|
6796
6831
|
clear_task_context,
|
|
6797
6832
|
generate_summary_filename,
|
|
6798
|
-
parse_summary_file,
|
|
6799
6833
|
status_to_exit_code,
|
|
6800
6834
|
wait_for_completion_signal,
|
|
6801
6835
|
write_task_context,
|
|
@@ -6867,8 +6901,8 @@ def _run_ensure_task(args, session, task, ctx, shell, project_path, timeout, jso
|
|
|
6867
6901
|
except TemplateError as e:
|
|
6868
6902
|
return _output_result(False, json_mode, str(e), exit_code=ENSURE_EXIT_PRE_FAILURE)
|
|
6869
6903
|
|
|
6870
|
-
# Generate summary filename
|
|
6871
|
-
summary_filename = generate_summary_filename(task.name)
|
|
6904
|
+
# Generate summary filename (scoped to session to avoid collisions)
|
|
6905
|
+
summary_filename = generate_summary_filename(session, task.name)
|
|
6872
6906
|
summary_path = project_path / summary_filename
|
|
6873
6907
|
ctx.summary_file = summary_filename
|
|
6874
6908
|
|
|
@@ -6880,7 +6914,7 @@ def _run_ensure_task(args, session, task, ctx, shell, project_path, timeout, jso
|
|
|
6880
6914
|
clear_task_context(session)
|
|
6881
6915
|
|
|
6882
6916
|
# Write task context for hook coordination
|
|
6883
|
-
# Hook will: first idle → send summary prompt
|
|
6917
|
+
# Hook will: first idle → send summary prompt (ensure polls for summary file directly)
|
|
6884
6918
|
write_task_context(
|
|
6885
6919
|
session=session,
|
|
6886
6920
|
task_name=task.name,
|
|
@@ -6889,6 +6923,18 @@ def _run_ensure_task(args, session, task, ctx, shell, project_path, timeout, jso
|
|
|
6889
6923
|
exit_on_complete=task.exit_on_complete,
|
|
6890
6924
|
)
|
|
6891
6925
|
|
|
6926
|
+
# Find previous summaries for this task to give the agent context
|
|
6927
|
+
summary_glob = f".agentwire/task-summary-{session}-{task.name}-*.md"
|
|
6928
|
+
prev_summaries = sorted(
|
|
6929
|
+
project_path.glob(summary_glob),
|
|
6930
|
+
key=lambda p: p.stat().st_mtime,
|
|
6931
|
+
reverse=True,
|
|
6932
|
+
)[:5]
|
|
6933
|
+
if prev_summaries:
|
|
6934
|
+
prompt += "\n\nPrevious task summaries (consider them when generating your output):"
|
|
6935
|
+
for p in prev_summaries:
|
|
6936
|
+
prompt += f"\n- {p}"
|
|
6937
|
+
|
|
6892
6938
|
if not json_mode:
|
|
6893
6939
|
print("Sending task prompt...")
|
|
6894
6940
|
|
|
@@ -6897,13 +6943,20 @@ def _run_ensure_task(args, session, task, ctx, shell, project_path, timeout, jso
|
|
|
6897
6943
|
|
|
6898
6944
|
# Wait for completion signal from hook
|
|
6899
6945
|
if not json_mode:
|
|
6900
|
-
print("Waiting for task completion
|
|
6946
|
+
print("Waiting for task completion...")
|
|
6901
6947
|
|
|
6902
6948
|
try:
|
|
6903
|
-
signal = wait_for_completion_signal(
|
|
6949
|
+
signal = wait_for_completion_signal(
|
|
6950
|
+
session, timeout=timeout, summary_path=summary_path
|
|
6951
|
+
)
|
|
6904
6952
|
last_status = signal.get("status", "incomplete")
|
|
6953
|
+
last_summary = signal.get("summary", "")
|
|
6954
|
+
ctx.status = last_status
|
|
6955
|
+
ctx.summary = last_summary
|
|
6905
6956
|
except CompletionTimeout:
|
|
6906
|
-
|
|
6957
|
+
# Don't clear task context here — the hook may still need it.
|
|
6958
|
+
# Hook cleans up after itself (exit_on_complete kills session).
|
|
6959
|
+
# Task context files are cleared at the START of next run.
|
|
6907
6960
|
last_status = "incomplete"
|
|
6908
6961
|
last_summary = "Timeout waiting for task completion"
|
|
6909
6962
|
if attempt < max_attempts:
|
|
@@ -6916,17 +6969,6 @@ def _run_ensure_task(args, session, task, ctx, shell, project_path, timeout, jso
|
|
|
6916
6969
|
# Clean up task context
|
|
6917
6970
|
clear_task_context(session)
|
|
6918
6971
|
|
|
6919
|
-
# Parse summary file
|
|
6920
|
-
try:
|
|
6921
|
-
result = parse_summary_file(summary_path)
|
|
6922
|
-
last_status = result.status
|
|
6923
|
-
last_summary = result.summary
|
|
6924
|
-
ctx.status = result.status
|
|
6925
|
-
ctx.summary = result.summary
|
|
6926
|
-
except Exception as e:
|
|
6927
|
-
last_status = "incomplete"
|
|
6928
|
-
last_summary = f"Failed to parse summary: {e}"
|
|
6929
|
-
|
|
6930
6972
|
if not json_mode:
|
|
6931
6973
|
print(f"Task status: {last_status}")
|
|
6932
6974
|
if last_summary:
|
|
@@ -7070,18 +7112,13 @@ def cmd_task_list(args) -> int:
|
|
|
7070
7112
|
session = getattr(args, 'session', None)
|
|
7071
7113
|
json_mode = getattr(args, 'json', False)
|
|
7072
7114
|
|
|
7073
|
-
# Find project path
|
|
7074
|
-
config = load_config()
|
|
7075
|
-
projects_dir = Path(config.get("projects", {}).get("dir", "~/projects")).expanduser()
|
|
7076
|
-
|
|
7115
|
+
# Find project path from session's working directory or cwd
|
|
7077
7116
|
if session:
|
|
7078
|
-
|
|
7079
|
-
project_path = projects_dir / project
|
|
7117
|
+
project_path = _get_session_project_path(session)
|
|
7080
7118
|
else:
|
|
7081
|
-
# Use current directory
|
|
7082
7119
|
project_path = Path.cwd()
|
|
7083
7120
|
|
|
7084
|
-
if not project_path.exists():
|
|
7121
|
+
if not project_path or not project_path.exists():
|
|
7085
7122
|
return _output_result(False, json_mode, f"Project path not found: {project_path}")
|
|
7086
7123
|
|
|
7087
7124
|
tasks = list_tasks(project_path)
|
|
@@ -7119,13 +7156,9 @@ def cmd_task_show(args) -> int:
|
|
|
7119
7156
|
session = None
|
|
7120
7157
|
task_name = task_arg
|
|
7121
7158
|
|
|
7122
|
-
# Find project path
|
|
7123
|
-
config = load_config()
|
|
7124
|
-
projects_dir = Path(config.get("projects", {}).get("dir", "~/projects")).expanduser()
|
|
7125
|
-
|
|
7159
|
+
# Find project path from session's working directory or cwd
|
|
7126
7160
|
if session:
|
|
7127
|
-
|
|
7128
|
-
project_path = projects_dir / project
|
|
7161
|
+
project_path = _get_session_project_path(session)
|
|
7129
7162
|
else:
|
|
7130
7163
|
project_path = Path.cwd()
|
|
7131
7164
|
|
|
@@ -7207,13 +7240,9 @@ def cmd_task_validate(args) -> int:
|
|
|
7207
7240
|
session = None
|
|
7208
7241
|
task_name = task_arg
|
|
7209
7242
|
|
|
7210
|
-
# Find project path
|
|
7211
|
-
config = load_config()
|
|
7212
|
-
projects_dir = Path(config.get("projects", {}).get("dir", "~/projects")).expanduser()
|
|
7213
|
-
|
|
7243
|
+
# Find project path from session's working directory or cwd
|
|
7214
7244
|
if session:
|
|
7215
|
-
|
|
7216
|
-
project_path = projects_dir / project
|
|
7245
|
+
project_path = _get_session_project_path(session)
|
|
7217
7246
|
else:
|
|
7218
7247
|
project_path = Path.cwd()
|
|
7219
7248
|
|
|
@@ -8003,6 +8032,7 @@ def main() -> int:
|
|
|
8003
8032
|
ensure_parser.add_argument("--dry-run", action="store_true", help="Show what would execute without running")
|
|
8004
8033
|
ensure_parser.add_argument("--wait-lock", action="store_true", help="Wait for lock instead of failing if locked")
|
|
8005
8034
|
ensure_parser.add_argument("--lock-timeout", type=int, default=60, help="Max time to wait for lock (default: 60s)")
|
|
8035
|
+
ensure_parser.add_argument("--skip-if-locked", action="store_true", help="Exit 0 silently if session is locked (for cron use cases)")
|
|
8006
8036
|
ensure_parser.add_argument("--json", action="store_true", help="Output JSON")
|
|
8007
8037
|
ensure_parser.set_defaults(func=cmd_ensure)
|
|
8008
8038
|
|
|
@@ -66,17 +66,21 @@ Status meanings:
|
|
|
66
66
|
Write the file now."""
|
|
67
67
|
|
|
68
68
|
|
|
69
|
-
def generate_summary_filename(task_name: str) -> str:
|
|
70
|
-
"""Generate a timestamped summary filename.
|
|
69
|
+
def generate_summary_filename(session: str, task_name: str) -> str:
|
|
70
|
+
"""Generate a session-scoped timestamped summary filename.
|
|
71
|
+
|
|
72
|
+
Includes session name so multiple sessions sharing a project directory
|
|
73
|
+
don't collide on summary files or trigger false TASK-ORPHAN detection.
|
|
71
74
|
|
|
72
75
|
Args:
|
|
73
|
-
|
|
76
|
+
session: tmux session name
|
|
77
|
+
task_name: Task name (for context)
|
|
74
78
|
|
|
75
79
|
Returns:
|
|
76
|
-
Relative path like .agentwire/task-summary-2024-01-15T07-00-00.md
|
|
80
|
+
Relative path like .agentwire/task-summary-mysession-2024-01-15T07-00-00.md
|
|
77
81
|
"""
|
|
78
82
|
timestamp = datetime.now().strftime("%Y-%m-%dT%H-%M-%S")
|
|
79
|
-
return f".agentwire/task-summary-{timestamp}.md"
|
|
83
|
+
return f".agentwire/task-summary-{session}-{task_name}-{timestamp}.md"
|
|
80
84
|
|
|
81
85
|
|
|
82
86
|
# =============================================================================
|
|
@@ -183,44 +187,28 @@ def clear_task_context(session: str) -> None:
|
|
|
183
187
|
pass
|
|
184
188
|
|
|
185
189
|
|
|
186
|
-
def write_completion_signal(session: str, status: str, summary_file: str) -> Path:
|
|
187
|
-
"""Write completion signal file (called by hook).
|
|
188
|
-
|
|
189
|
-
Args:
|
|
190
|
-
session: tmux session name
|
|
191
|
-
status: Task status from summary
|
|
192
|
-
summary_file: Path to summary file
|
|
193
|
-
|
|
194
|
-
Returns:
|
|
195
|
-
Path to the completion signal file
|
|
196
|
-
"""
|
|
197
|
-
TASKS_DIR.mkdir(parents=True, exist_ok=True)
|
|
198
|
-
|
|
199
|
-
signal = {
|
|
200
|
-
"completed_at": datetime.now().isoformat(),
|
|
201
|
-
"status": status,
|
|
202
|
-
"summary_file": summary_file,
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
complete_file = TASKS_DIR / f"{session}.complete"
|
|
206
|
-
complete_file.write_text(json.dumps(signal, indent=2))
|
|
207
|
-
return complete_file
|
|
208
|
-
|
|
209
|
-
|
|
210
190
|
def wait_for_completion_signal(
|
|
211
191
|
session: str,
|
|
212
192
|
timeout: float = 300.0,
|
|
213
193
|
poll_interval: float = 2.0,
|
|
194
|
+
summary_path: Path | None = None,
|
|
214
195
|
) -> dict:
|
|
215
|
-
"""Wait for completion
|
|
196
|
+
"""Wait for task completion by polling the summary file directly.
|
|
197
|
+
|
|
198
|
+
Primary detection: polls for the summary file the agent writes after
|
|
199
|
+
receiving the summary prompt. When found, parses it and returns the result.
|
|
200
|
+
|
|
201
|
+
Fallback: also checks for the legacy .complete signal file in case the
|
|
202
|
+
hook writes one (e.g. manual trigger).
|
|
216
203
|
|
|
217
204
|
Args:
|
|
218
205
|
session: tmux session name
|
|
219
206
|
timeout: Maximum seconds to wait
|
|
220
207
|
poll_interval: Seconds between checks
|
|
208
|
+
summary_path: Path to the summary .md file the agent will write
|
|
221
209
|
|
|
222
210
|
Returns:
|
|
223
|
-
|
|
211
|
+
Dict with 'status', 'summary', 'summary_file' keys
|
|
224
212
|
|
|
225
213
|
Raises:
|
|
226
214
|
CompletionTimeout: If timeout exceeded
|
|
@@ -229,6 +217,21 @@ def wait_for_completion_signal(
|
|
|
229
217
|
start_time = time.time()
|
|
230
218
|
|
|
231
219
|
while True:
|
|
220
|
+
# Primary: check if the agent has written the summary file
|
|
221
|
+
if summary_path and summary_path.exists() and summary_path.stat().st_size > 0:
|
|
222
|
+
# Give a moment for the file to be fully written
|
|
223
|
+
time.sleep(0.5)
|
|
224
|
+
try:
|
|
225
|
+
result = parse_summary_file(summary_path)
|
|
226
|
+
return {
|
|
227
|
+
"status": result.status,
|
|
228
|
+
"summary": result.summary,
|
|
229
|
+
"summary_file": str(summary_path),
|
|
230
|
+
}
|
|
231
|
+
except CompletionError:
|
|
232
|
+
pass # File may be partially written, retry
|
|
233
|
+
|
|
234
|
+
# Fallback: check for legacy .complete signal file
|
|
232
235
|
if complete_file.exists():
|
|
233
236
|
try:
|
|
234
237
|
signal = json.loads(complete_file.read_text())
|
|
@@ -397,18 +400,26 @@ def wait_for_file(
|
|
|
397
400
|
|
|
398
401
|
|
|
399
402
|
def parse_summary_file(path: Path) -> SummaryResult:
|
|
400
|
-
"""Parse a task summary file
|
|
403
|
+
"""Parse a task summary file.
|
|
401
404
|
|
|
402
|
-
|
|
405
|
+
Supports two formats:
|
|
406
|
+
|
|
407
|
+
1. YAML front matter (from Python SYSTEM_SUMMARY_PROMPT):
|
|
403
408
|
---
|
|
404
409
|
status: complete
|
|
405
410
|
summary: Did the thing
|
|
406
411
|
files_modified:
|
|
407
412
|
- path/to/file
|
|
408
|
-
blockers: []
|
|
409
413
|
---
|
|
410
414
|
|
|
411
|
-
|
|
415
|
+
2. Markdown headings (from hook summary prompt):
|
|
416
|
+
# Task Summary
|
|
417
|
+
## Status
|
|
418
|
+
complete
|
|
419
|
+
## What Was Done
|
|
420
|
+
Description here
|
|
421
|
+
## Notes
|
|
422
|
+
Extra context
|
|
412
423
|
|
|
413
424
|
Args:
|
|
414
425
|
path: Path to the summary file
|
|
@@ -430,9 +441,8 @@ def parse_summary_file(path: Path) -> SummaryResult:
|
|
|
430
441
|
files_modified: list[str] = []
|
|
431
442
|
blockers: list[str] = []
|
|
432
443
|
|
|
433
|
-
# Parse YAML front matter
|
|
434
444
|
if content.startswith("---"):
|
|
435
|
-
#
|
|
445
|
+
# Parse YAML front matter format
|
|
436
446
|
end_match = re.search(r"\n---\s*\n", content[3:])
|
|
437
447
|
if end_match:
|
|
438
448
|
yaml_content = content[3:3 + end_match.start()]
|
|
@@ -461,10 +471,31 @@ def parse_summary_file(path: Path) -> SummaryResult:
|
|
|
461
471
|
files_modified.append(item)
|
|
462
472
|
elif current_list == "blockers":
|
|
463
473
|
blockers.append(item)
|
|
464
|
-
|
|
465
|
-
|
|
474
|
+
else:
|
|
475
|
+
# Parse markdown heading format (## Status, ## What Was Done, etc.)
|
|
476
|
+
sections: dict[str, list[str]] = {}
|
|
477
|
+
current_section: str | None = None
|
|
478
|
+
|
|
479
|
+
for line in content.split("\n"):
|
|
480
|
+
stripped = line.strip()
|
|
481
|
+
heading = re.match(r"^#{1,3}\s+(.+)", stripped)
|
|
482
|
+
if heading:
|
|
483
|
+
current_section = heading.group(1).lower()
|
|
484
|
+
continue
|
|
485
|
+
if current_section and stripped:
|
|
486
|
+
sections.setdefault(current_section, []).append(stripped)
|
|
487
|
+
|
|
488
|
+
if "status" in sections:
|
|
489
|
+
status = sections["status"][0].strip().lower()
|
|
490
|
+
if "what was done" in sections:
|
|
491
|
+
summary = " ".join(sections["what was done"])
|
|
492
|
+
elif "summary" in sections:
|
|
493
|
+
summary = " ".join(sections["summary"])
|
|
494
|
+
|
|
495
|
+
# Validate status — also accept "error" as "failed"
|
|
496
|
+
if status == "error":
|
|
497
|
+
status = "failed"
|
|
466
498
|
if status not in ("complete", "incomplete", "failed"):
|
|
467
|
-
# Default to incomplete if status is unrecognized
|
|
468
499
|
status = "incomplete"
|
|
469
500
|
|
|
470
501
|
return SummaryResult(
|
|
@@ -190,18 +190,18 @@ complete | incomplete | error
|
|
|
190
190
|
$AGENTWIRE send -s "$tmux_session" "$instruction" >/dev/null 2>&1 &
|
|
191
191
|
echo "[$(date -Iseconds)] TASK: summary prompt sent" >> "$dlog"
|
|
192
192
|
else
|
|
193
|
-
# Second+ idle:
|
|
194
|
-
echo "[$(date -Iseconds)] TASK: second idle
|
|
195
|
-
|
|
196
|
-
# Write completion signal file
|
|
197
|
-
complete_file="$HOME/.agentwire/tasks/${tmux_session}.complete"
|
|
198
|
-
echo "{\"completed_at\":\"$(date -Iseconds)\",\"status\":\"complete\",\"summary_file\":\"${summary_file}\"}" > "$complete_file"
|
|
193
|
+
# Second+ idle: optionally exit session (ensure polls summary file directly)
|
|
194
|
+
echo "[$(date -Iseconds)] TASK: second idle" >> "$dlog"
|
|
199
195
|
|
|
200
196
|
if [[ "$exit_on_complete" == "true" ]]; then
|
|
201
197
|
echo "[$(date -Iseconds)] TASK: exit_on_complete=true, sending /exit" >> "$dlog"
|
|
202
198
|
sleep 1
|
|
203
199
|
$AGENTWIRE send -s "$tmux_session" "/exit" >/dev/null 2>&1
|
|
204
200
|
|
|
201
|
+
# Clean up task context file so it doesn't haunt future sessions
|
|
202
|
+
rm "$task_context_file" 2>/dev/null
|
|
203
|
+
echo "[$(date -Iseconds)] TASK: cleaned up task context" >> "$dlog"
|
|
204
|
+
|
|
205
205
|
# Wait for Claude to exit, then kill the tmux session
|
|
206
206
|
sleep 3
|
|
207
207
|
echo "[$(date -Iseconds)] TASK: killing tmux session" >> "$dlog"
|
|
@@ -211,8 +211,9 @@ complete | incomplete | error
|
|
|
211
211
|
) &
|
|
212
212
|
else
|
|
213
213
|
# No task context file - check if this might be a scheduled task that lost its context
|
|
214
|
-
# Look for recent task summary files
|
|
215
|
-
|
|
214
|
+
# Look for recent task summary files scoped to THIS session (avoids false matches
|
|
215
|
+
# when multiple sessions share the same project directory)
|
|
216
|
+
recent_summary=$(find "${cwd}/.agentwire" -name "task-summary-${tmux_session}-*.md" -mmin -5 2>/dev/null | head -1)
|
|
216
217
|
|
|
217
218
|
if [[ -n "$recent_summary" ]]; then
|
|
218
219
|
log "No task context but found recent summary file, cleaning up session"
|
|
@@ -222,6 +223,11 @@ complete | incomplete | error
|
|
|
222
223
|
echo "[$(date -Iseconds)] TASK-ORPHAN: found summary at $recent_summary, exiting session" >> "$dlog"
|
|
223
224
|
sleep 1
|
|
224
225
|
$AGENTWIRE send -s "$tmux_session" "/exit" >/dev/null 2>&1
|
|
226
|
+
|
|
227
|
+
# Clean up orphan summary so it doesn't trigger again
|
|
228
|
+
rm "$recent_summary" 2>/dev/null
|
|
229
|
+
echo "[$(date -Iseconds)] TASK-ORPHAN: cleaned up orphan summary" >> "$dlog"
|
|
230
|
+
|
|
225
231
|
sleep 3
|
|
226
232
|
echo "[$(date -Iseconds)] TASK-ORPHAN: killing tmux session" >> "$dlog"
|
|
227
233
|
tmux kill-session -t "$tmux_session" 2>/dev/null &
|
|
@@ -100,6 +100,15 @@ def session_lock(
|
|
|
100
100
|
f"Timeout waiting for lock on session '{session}' "
|
|
101
101
|
f"after {timeout:.1f}s"
|
|
102
102
|
)
|
|
103
|
+
# Check if the lock holder is still alive
|
|
104
|
+
holder_pid = get_lock_holder(session)
|
|
105
|
+
if holder_pid is not None and not _is_process_running(holder_pid):
|
|
106
|
+
# Stale lock from a dead process — clean it up
|
|
107
|
+
remove_lock(session)
|
|
108
|
+
# Close our current file handle (the old lock file is gone)
|
|
109
|
+
lock_file.close()
|
|
110
|
+
lock_file = open(lock_path, "w")
|
|
111
|
+
continue # Retry immediately
|
|
103
112
|
time.sleep(poll_interval)
|
|
104
113
|
else:
|
|
105
114
|
# Non-blocking - fail immediately if locked
|
|
@@ -588,6 +588,21 @@ def say(text: str, session: str | None = None, voice: str | None = None) -> str:
|
|
|
588
588
|
Returns:
|
|
589
589
|
Success message or error description.
|
|
590
590
|
"""
|
|
591
|
+
# Quick TTS health check — fail fast if server is unreachable
|
|
592
|
+
try:
|
|
593
|
+
from .config import load_config as load_typed_config
|
|
594
|
+
from .network import NetworkContext
|
|
595
|
+
import urllib.request
|
|
596
|
+
|
|
597
|
+
cfg = load_typed_config()
|
|
598
|
+
if cfg.tts.backend not in ("runpod", "none"):
|
|
599
|
+
ctx = NetworkContext.from_config()
|
|
600
|
+
tts_url = ctx.get_service_url("tts", use_tunnel=True)
|
|
601
|
+
urllib.request.urlopen(f"{tts_url}/health", timeout=3)
|
|
602
|
+
except Exception as e:
|
|
603
|
+
url = locals().get("tts_url", "unknown")
|
|
604
|
+
return f"TTS server unreachable at {url}: {e}"
|
|
605
|
+
|
|
591
606
|
args = ["say"]
|
|
592
607
|
if session:
|
|
593
608
|
args.extend(["-s", session])
|
|
@@ -598,7 +613,11 @@ def say(text: str, session: str | None = None, voice: str | None = None) -> str:
|
|
|
598
613
|
# Say command doesn't return JSON, run without --json
|
|
599
614
|
data = run_agentwire_cmd(args, json_output=False)
|
|
600
615
|
if data.get("success"):
|
|
601
|
-
|
|
616
|
+
from .utils.chunker import chunk_text
|
|
617
|
+
chunks = chunk_text(text)
|
|
618
|
+
if len(chunks) > 1:
|
|
619
|
+
return f"Queued speech ({len(chunks)} chunks)."
|
|
620
|
+
return "Queued speech."
|
|
602
621
|
return f"Failed to speak: {data.get('error', 'Unknown error')}"
|
|
603
622
|
|
|
604
623
|
|