agentwire-dev 1.53.0__tar.gz → 1.55.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.53.0 → agentwire_dev-1.55.0}/PKG-INFO +1 -1
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/__init__.py +1 -1
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/__main__.py +2 -1
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/config.py +4 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/config_cli.py +1 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/core.py +26 -2
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/bash-tool-damage-control.py +197 -5
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/edit-tool-damage-control.py +197 -5
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/mcp-tool-damage-control.py +197 -5
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/read-tool-damage-control.py +197 -5
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/rules/git.yaml +14 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/write-tool-damage-control.py +197 -5
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/inbox.py +103 -8
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_msg.py +20 -6
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_worktree.py +30 -4
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/msg_cli.py +35 -8
- agentwire_dev-1.55.0/agentwire/palette_cli.py +188 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/pane_manager.py +8 -1
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/prompt_router.py +87 -2
- agentwire_dev-1.55.0/agentwire/routes/palette.py +79 -0
- agentwire_dev-1.55.0/agentwire/safety/_core.py +1336 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/scheduler/__init__.py +1 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/scheduler/dispatch.py +120 -3
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/scheduler/schedule.py +12 -2
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/server.py +28 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/session_cli.py +37 -9
- agentwire_dev-1.55.0/agentwire/session_ready.py +641 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/command-palette.js +106 -2
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/desktop.js +22 -29
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/shortcuts.js +1 -1
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/voice_status.py +52 -5
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/sessions/messaging.md +68 -9
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/conftest.py +40 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/integration/test_send_verified_tmux.py +120 -28
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_cli_commands.py +77 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_control_plane_protection.py +35 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_damage_control_hooks.py +91 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_inbox.py +165 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_mcp_tools_args.py +63 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_msg_cli.py +96 -2
- agentwire_dev-1.55.0/tests/unit/test_palette_cli.py +133 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_portal_api.py +26 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_prompt_router.py +136 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_routes_snapshot.py +3 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_scheduler.py +124 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_session_ready.py +407 -49
- agentwire_dev-1.55.0/tests/unit/test_unattended_env_isolation.py +90 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_voice_status.py +68 -0
- agentwire_dev-1.55.0/tests/unit/test_window_cycling_binding.py +65 -0
- agentwire_dev-1.53.0/agentwire/safety/_core.py +0 -1144
- agentwire_dev-1.53.0/agentwire/session_ready.py +0 -413
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/.agentwire.yml.example +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/.claude/commands/handoff.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/.claude/skills/wiki/SKILL.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/.github/FUNDING.yml +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/.github/ISSUE_TEMPLATE/question.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/.github/workflows/pytest.yml +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/.github/workflows/ruff.yml +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/.github/workflows/security.yml +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/.github/workflows/tts-smoke.yml +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/.gitignore +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/AGENTS.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/CODE_OF_CONDUCT.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/CONTRIBUTING.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/Dockerfile.local +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/LICENSE +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/README.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/RELEASING.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/SECURITY.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/SPONSORS.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/agents/__init__.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/agents/base.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/agents/tmux.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/cached_status.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/channels/__init__.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/channels/base.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/channels/email.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/channels/push.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/channels/quo.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/channels_cli.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/completion.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/council/__init__.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/council/cli.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/council/inbox.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/council/state.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/council/view.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/devices.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/diff_cli.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/doctor_cli.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/doctor_voice.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/ensure_cli.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/fetch.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/handoff/__init__.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/handoff/git_state.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/handoff/instructions.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/handoff/parser.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/handoff/renderer.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/handoff/schema.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/handoff_cli.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/history.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/history_cli.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/__init__.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/agentwire-permission.sh +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/__init__.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/audit_logger.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/rules/agentwire.yaml +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/rules/aws.yaml +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/rules/cloud-hosting.yaml +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/rules/containers.yaml +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/rules/control-plane.yaml +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/rules/core.yaml +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/rules/databases.yaml +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/rules/firebase.yaml +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/rules/gcp.yaml +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/rules/gws.yaml +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/rules/infrastructure.yaml +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/rules/outbound.yaml +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/rules/publish.yaml +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/damage-control/rules/remote.yaml +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/idle-handler.sh +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks/queue-processor.sh +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/hooks_cli.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/limits_cli.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/listen.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/locking.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/machine_cli.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_channels.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_cli.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_core.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_council.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_desktop.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_handoff.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_history.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_listen.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_lock.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_machine.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_notify.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_pane.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_projects.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_scheduler.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_scratchpad.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_server.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_services.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_session.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_status.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_task.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_tunnels.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_voice.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_voiceclone.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/mcp_wiki.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/network.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/notify_cli.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/onboarding.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/pane_cli.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/portal_cli.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/project_config.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/projects.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/prompts_cli.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/push_cli.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/push_store.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/repo_cli.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/research.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/__init__.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/agentwire.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/anchor.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/chatbot.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/contributor.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/correspondent.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/council-brain.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/council-conscience.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/council-critic.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/council-devils-advocate.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/council-gut.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/council-historian.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/council-member.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/council-orchestrator.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/init.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/notifications.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/orchestrator.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/soul.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/task-runner.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/voice.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/worker.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles/worktree-session.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/roles_cli.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/routes/__init__.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/routes/artifacts.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/routes/config.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/routes/council.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/routes/desktop.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/routes/history.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/routes/machines.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/routes/notify.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/routes/permission.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/routes/projects.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/routes/push.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/routes/safety.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/routes/scheduler.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/routes/scratchpad.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/routes/sessions.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/routes/sessions_admin.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/routes/voice.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/safety/__init__.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/safety_cli.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/safety_commands.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/scheduler/gates.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/scheduler/loop.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/scheduler/models.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/scheduler/report.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/scheduler/state.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/scheduler_cli.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/scratchpad.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/security.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/send_cli.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/services.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/session_context.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/ssh.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/agentwire-splash-logo-layers--agentwire-text.png +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/agentwire-splash-logo-layers--echo-claw-fg.png +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/agentwire-splash-logo-layers--echo.png +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/agentwire-splash-logo-layers--telephone.png +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/agentwire-splash-logo-layers--tree.png +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/announcements.json +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/apple-touch-icon.png +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/css/desktop.css +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/css/mobile.css +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/favicon-32.png +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/favicon-64.png +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/icons/sessions/bear.webp +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/icons/sessions/cat.webp +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/icons/sessions/crown.webp +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/icons/sessions/custom/agentwire-portal.webp +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/icons/sessions/custom/agentwire-tts.webp +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/icons/sessions/custom/agentwire.webp +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/icons/sessions/deer.webp +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/icons/sessions/drone.webp +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/icons/sessions/eagle.webp +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/icons/sessions/fox.webp +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/icons/sessions/hawk.webp +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/icons/sessions/horse.webp +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/icons/sessions/lion.webp +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/icons/sessions/rabbit.webp +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/icons/sessions/robot.webp +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/icons/sessions/tiger.webp +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/icons/sessions/wolf.webp +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/img/icon-192.png +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/img/icon-512.png +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/.gitkeep +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/announcement-modal.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/api.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/artifact-window.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/collage.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/council-window.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/dead-key-suppressor.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/desktop-manager.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/help-modal.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/icon-manager.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/mobile.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/notification-prefs.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/notifications-panel.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/ptt.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/push-client.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/review-window.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/safety-shared.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/safety-window.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/scratchpad.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/service-classification.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/session-id.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/session-window.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/sidebar/artifacts-section.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/sidebar/config-section.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/sidebar/council-section.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/sidebar/machines-section.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/sidebar/projects-section.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/sidebar/safety-section.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/sidebar/scheduler-section.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/sidebar/services-section.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/sidebar/sessions-section.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/sidebar.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/terminal-font-prefs.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/tile-manager.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/toast.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/token-modal.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/utils/ansi.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/utils/swipe.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/voice/autosend-prefs.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/voice/browser-stt.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/voice/browser-tts.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/voice/jargon.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/voice/prompt.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/js/winbox.bundle.min.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/manifest.webmanifest +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/static/service-worker.js +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/stt/__init__.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/stt/base.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/stt/cloud.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/stt/engine.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/stt/local.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/stt/server_backend.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/stt/stt_server.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/system_cli.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tasks.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/templates/__init__.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/templates/base.html +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/templates/desktop.html +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/templates/email_notification.html +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/templates/handoff/show-the-story.html.j2 +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/templates/handoff/theme.css.j2 +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/templates/mobile.html +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/templates/pair.html +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/templates/tmux.conf +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/templating.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tooldefs/aws.yaml +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tooldefs/docker.yaml +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tooldefs/gcp.yaml +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tooldefs/gh.yaml +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tooldefs/git.yaml +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tooldefs/gws.yaml +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tooldefs/kubectl.yaml +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tooldefs/npm.yaml +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tooldefs/terraform.yaml +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tooldefs/uv.yaml +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tts/__init__.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tts/audio.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tts/base.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tts/engines/__init__.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tts/engines/chatterbox.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tts/engines/kokoro.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tts/engines/zonos.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tts/kokoro_server.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tts/local.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tts/registry.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tts_cli.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tts_server.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tunnels.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/tunnels_cli.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/usage_limit.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/utils/__init__.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/utils/chunker.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/utils/event_log.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/utils/file_io.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/utils/paths.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/utils/speech.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/utils/subprocess.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/validation.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/voiceclone.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/wiki.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/wiki_audit.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/wiki_cli.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/worktree.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/agentwire/worktree_registry.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/decisions/obsidian-second-brain.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/logo.png +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/INDEX.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/architecture.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/briefing-mode.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/communication/channels.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/communication/hammerspoon.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/communication/handoff.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/concepts.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/council.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/deployment/remote-access.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/deployment/remote-machines.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/glossary.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/integrations/gws-google-workspace-cli.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/internals/damage-control.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/internals/parallel-refactor.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/internals/portal.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/internals/shell-escaping.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/internals/troubleshooting.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/internals/window-collage.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/quickstart.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/research/briefing-mode-feasibility.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/research/orchestration-transport-alternatives.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/scheduling/scheduled-workloads.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/security/damage-control-hardening.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/security/pip-audit.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/security/remote-access-hardening.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/security/secrets.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/services.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/sessions/claude-code-auto-mode.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/sessions/pi.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/sessions/prompt-routing.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/sessions/window-sizing.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/sessions/worktree-sessions.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/usage-limit-recovery.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/voice/shim-contract.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/voice/stt-cloud.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/voice/stt-self-hosted.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/docs/wiki/voice/tts-self-hosted.md +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/pyproject.toml +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/e2e/test_portal_ui.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/fixtures/sample_agentwire.yml +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/fixtures/sample_config.yaml +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/fixtures/sample_scheduler.yaml +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/integration/test_council_portal.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/integration/test_scheduler_board.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/integration/test_server_websockets.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/integration/test_worktree_cmd.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_announcements.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_build_agent_command.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_channels.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_cli_output.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_cli_smoke.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_config.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_config_cli.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_council_cli.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_council_inbox.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_council_state.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_council_view.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_damage_control_bypass.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_damage_control_sync.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_devices.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_diff_cli.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_doctor_voice.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_event_log.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_file_io.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_handoff_git_state.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_handoff_instructions.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_handoff_parser.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_handoff_renderer.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_history.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_hooks_install.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_idle_handler.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_idle_nag.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_init_command.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_install_parity.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_limits_tick_isolation.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_locking.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_mcp_server.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_notify_cli.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_onboarding_reinit.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_pip_environment.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_project_config.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_push.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_rebuild_guard.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_repo_cli.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_roles.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_safety_commands.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_safety_disabled_rules.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_safety_escape_hatch.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_safety_heal.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_safety_kill_switch.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_scheduler_parsing.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_scratchpad.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_security.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_server_async.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_server_pure.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_services.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_session_context.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_shared_dir_guard.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_skill_install.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_speech_tags.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_ssh.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_stt_backend.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_stt_cloud.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_stt_engine.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_task_cli.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_tasks.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_templating.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_terminal_resize.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_tmux_template.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_tts_engine_resolution.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_tts_local.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_tunnels_probe.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_usage_limit.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_validation.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_wiki.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_wiki_audit.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_worktree.py +0 -0
- {agentwire_dev-1.53.0 → agentwire_dev-1.55.0}/tests/unit/test_worktree_registry.py +0 -0
|
@@ -191,7 +191,7 @@ Command Categories:
|
|
|
191
191
|
from . import scheduler_cli, ensure_cli
|
|
192
192
|
from . import doctor_cli, history_cli, machine_cli, safety_cli
|
|
193
193
|
from . import handoff_cli, hooks_cli, mcp_cli, roles_cli, tunnels_cli
|
|
194
|
-
from . import notify_cli, push_cli, repo_cli, system_cli, wiki_cli
|
|
194
|
+
from . import notify_cli, palette_cli, push_cli, repo_cli, system_cli, wiki_cli
|
|
195
195
|
from .council import cli as council_cli
|
|
196
196
|
|
|
197
197
|
_REGISTRARS = [ # noqa: N806 # registry constant; Phase 1 of #495 appends here
|
|
@@ -223,6 +223,7 @@ Command Categories:
|
|
|
223
223
|
push_cli.register_push_parser,
|
|
224
224
|
repo_cli.register_repo_parser,
|
|
225
225
|
config_cli.register_config_parser,
|
|
226
|
+
palette_cli.register_palette_parser,
|
|
226
227
|
]
|
|
227
228
|
for _reg in _REGISTRARS:
|
|
228
229
|
_reg(subparsers)
|
|
@@ -431,6 +431,10 @@ class SchedulerConfig:
|
|
|
431
431
|
session_create_timeout: int = 30
|
|
432
432
|
max_loop_sleep: int = 60
|
|
433
433
|
dispatch_cooldown: int = 60
|
|
434
|
+
# Watchdog ceiling for a single dispatch (seconds). A hung `ensure`
|
|
435
|
+
# (e.g. an unrecognized usage-limit dialog) otherwise holds the single
|
|
436
|
+
# dispatch slot forever and starves the whole board (#677). 0 disables.
|
|
437
|
+
dispatch_max_runtime: int = 14400
|
|
434
438
|
# Crash-loop guard: a board that won't parse / has no tasks backs off
|
|
435
439
|
# exponentially from `error_backoff_base` up to `error_backoff_max`
|
|
436
440
|
# instead of tight-looping (#449).
|
|
@@ -78,12 +78,36 @@ class AgentCommand:
|
|
|
78
78
|
_UNATTENDED_ENV_KEYS = ("AGENTWIRE_UNATTENDED", "AGENTWIRE_UNATTENDED_ALLOW")
|
|
79
79
|
|
|
80
80
|
|
|
81
|
+
def _capture_unattended_env() -> dict[str, str]:
|
|
82
|
+
"""Pop the unattended marker OUT of this process's environment.
|
|
83
|
+
|
|
84
|
+
The marker must reach new sessions only via the deliberate
|
|
85
|
+
``tmux new-session -e K=V`` path (``_with_unattended_env`` below). If it
|
|
86
|
+
stays in ``os.environ``, a ``tmux`` client we spawn while no server is
|
|
87
|
+
running boots the shared tmux server WITH the marker in its process env,
|
|
88
|
+
and from then on every session that server creates — interactive human
|
|
89
|
+
sessions included — inherits it and gets falsely treated as unattended
|
|
90
|
+
(#674). Capturing at import time preserves intended propagation while
|
|
91
|
+
guaranteeing no child process of the CLI can inherit the raw var.
|
|
92
|
+
"""
|
|
93
|
+
captured: dict[str, str] = {}
|
|
94
|
+
for key in _UNATTENDED_ENV_KEYS:
|
|
95
|
+
val = os.environ.pop(key, None)
|
|
96
|
+
if val:
|
|
97
|
+
captured[key] = val
|
|
98
|
+
return captured
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
_UNATTENDED_ENV = _capture_unattended_env()
|
|
102
|
+
|
|
103
|
+
|
|
81
104
|
def _with_unattended_env(env: dict[str, str]) -> dict[str, str]:
|
|
82
105
|
"""Propagate the unattended marker into a session being created.
|
|
83
106
|
|
|
84
107
|
The scheduler is the single place that decides a dispatch is unattended —
|
|
85
108
|
it seeds ``AGENTWIRE_UNATTENDED[=1]`` (and any per-task
|
|
86
|
-
``AGENTWIRE_UNATTENDED_ALLOW``) into the dispatch subprocess environment
|
|
109
|
+
``AGENTWIRE_UNATTENDED_ALLOW``) into the dispatch subprocess environment,
|
|
110
|
+
captured here at import (see ``_capture_unattended_env``).
|
|
87
111
|
Every session-creation path funnels its env through here on the way to
|
|
88
112
|
``tmux new-session -e K=V``, so the marker lands in the new session BEFORE
|
|
89
113
|
the agent launches and the damage-control hook can read it. A child session
|
|
@@ -94,7 +118,7 @@ def _with_unattended_env(env: dict[str, str]) -> dict[str, str]:
|
|
|
94
118
|
"""
|
|
95
119
|
merged = dict(env)
|
|
96
120
|
for key in _UNATTENDED_ENV_KEYS:
|
|
97
|
-
val =
|
|
121
|
+
val = _UNATTENDED_ENV.get(key)
|
|
98
122
|
if val and key not in merged:
|
|
99
123
|
merged[key] = val
|
|
100
124
|
return merged
|
|
@@ -121,6 +121,25 @@ READ_ONLY_BLOCKED = (
|
|
|
121
121
|
|
|
122
122
|
NO_DELETE_BLOCKED = DELETE_PATTERNS
|
|
123
123
|
|
|
124
|
+
# Interpreter programs can write a file without any shell-level write token:
|
|
125
|
+
# ``python3 -c 'open(p, "w")...'``, a heredoc piped into ``python -``, or
|
|
126
|
+
# ``echo 'open(p,"w")' | python3``. The program text is opaque to static
|
|
127
|
+
# matching, so for PROTECTED control-plane paths we fail closed: an interpreter
|
|
128
|
+
# invocation that takes inline code (-c/-e, combined forms like ``perl -pie``),
|
|
129
|
+
# a heredoc/stdin program, or has data piped into it, AND mentions a protected
|
|
130
|
+
# path anywhere in the command, is blocked (#678). Applied ONLY by
|
|
131
|
+
# ``check_protected_command`` — never to user readOnlyPaths rules — so a
|
|
132
|
+
# legitimate read via ``cat``/``grep`` stays allowed.
|
|
133
|
+
_INTERPRETERS = r'(?:python[\d.]*|perl|ruby|node|deno|bun|php)'
|
|
134
|
+
INTERPRETER_WRITE_PATTERNS = [
|
|
135
|
+
# interpreter ... -c/-e/... or stdin/heredoc marker, path anywhere after
|
|
136
|
+
(r'\b' + _INTERPRETERS +
|
|
137
|
+
r'\b[^\n|;&]*(?:\s-\S*[ceE]\b|\s+-\s|\s+-$|<<)(?s:.*){path}',
|
|
138
|
+
"interpreter program"),
|
|
139
|
+
# path anywhere before a pipe into an interpreter
|
|
140
|
+
(r'{path}(?s:.*)\|\s*' + _INTERPRETERS + r'\b', "pipe into interpreter"),
|
|
141
|
+
]
|
|
142
|
+
|
|
124
143
|
|
|
125
144
|
# ============================================================================
|
|
126
145
|
# GLOB / PATH HELPERS
|
|
@@ -416,7 +435,11 @@ def check_path_patterns(
|
|
|
416
435
|
Returns ``(matched, reason)`` where reason names the operation that triggered.
|
|
417
436
|
"""
|
|
418
437
|
if is_glob_pattern(path):
|
|
419
|
-
|
|
438
|
+
# Allow an optional (possibly quoted) directory prefix: ``*`` in the
|
|
439
|
+
# glob can't span ``/``, so without this a basename glob like
|
|
440
|
+
# ``*.agentwire.yml`` never matched an absolute target
|
|
441
|
+
# (``> /repo/.agentwire.yml`` sailed past every write pattern) (#678).
|
|
442
|
+
path_regex = r'["\']?(?:[^\s"\';|&<>]*/)?' + glob_to_regex(path)
|
|
420
443
|
else:
|
|
421
444
|
expanded = os.path.expanduser(path)
|
|
422
445
|
if expanded == path:
|
|
@@ -484,6 +507,9 @@ def load_write_patterns_from_tooldefs(tooldefs_dir: Optional[Path]) -> List[Dict
|
|
|
484
507
|
"pattern": regex,
|
|
485
508
|
"reason": f"{tool_name}: {cmd_def.get('description', cmd_str)}",
|
|
486
509
|
"ask": True,
|
|
510
|
+
# Command-prefix rule: match command position only, never
|
|
511
|
+
# quoted argument/message content (#675).
|
|
512
|
+
"anchored": True,
|
|
487
513
|
}
|
|
488
514
|
# An explicit ``id:`` on the tooldef command yields a stable
|
|
489
515
|
# rule ID (e.g. ``git.push``) that survives description edits —
|
|
@@ -806,7 +832,10 @@ def check_protected_command(
|
|
|
806
832
|
for path in _protected_path_patterns():
|
|
807
833
|
for hay in haystacks:
|
|
808
834
|
matched, reason = check_path_patterns(
|
|
809
|
-
hay,
|
|
835
|
+
hay,
|
|
836
|
+
path,
|
|
837
|
+
READ_ONLY_BLOCKED + INTERPRETER_WRITE_PATTERNS,
|
|
838
|
+
"protected control-plane path",
|
|
810
839
|
)
|
|
811
840
|
if matched:
|
|
812
841
|
op = _infer_operation_from_reason(reason)
|
|
@@ -918,6 +947,165 @@ def normalize_subcommands(command: str) -> Tuple[List[str], Optional[str]]:
|
|
|
918
947
|
return subs, None
|
|
919
948
|
|
|
920
949
|
|
|
950
|
+
#
|
|
951
|
+
# CONTENT MASKING (#675)
|
|
952
|
+
#
|
|
953
|
+
# Tooldef rules are COMMAND-PREFIX rules (``\bgh\s+pr\s+comment\b``), but the
|
|
954
|
+
# haystacks above include quoted argument text — so a commit message that
|
|
955
|
+
# merely *mentions* "gh pr comment" false-matches the rule. Anchored rules are
|
|
956
|
+
# therefore matched against MASKED subcommands instead: quoted spans that can
|
|
957
|
+
# only be content (a quoted token containing whitespace — a message, an echo
|
|
958
|
+
# string, a heredoc body) are replaced with a placeholder, while quoted
|
|
959
|
+
# single words are kept (``"gh" pr comment`` is still a real command) and
|
|
960
|
+
# ``sh -c "…"`` payloads are recursively re-scanned as commands.
|
|
961
|
+
|
|
962
|
+
_MASK = "\x00"
|
|
963
|
+
_HEREDOC_RE = re.compile(r"<<-?\s*(['\"]?)([A-Za-z_]\w+)\1")
|
|
964
|
+
_SHELL_NAMES = {"sh", "bash", "zsh", "dash", "ksh"}
|
|
965
|
+
_ASSIGN_TOKEN_RE = re.compile(r"^[A-Za-z_]\w*=")
|
|
966
|
+
|
|
967
|
+
|
|
968
|
+
def _strip_heredoc_bodies(command: str) -> str:
|
|
969
|
+
"""Remove heredoc body lines (``<<EOF … EOF``) — they are content."""
|
|
970
|
+
m = _HEREDOC_RE.search(command)
|
|
971
|
+
if not m:
|
|
972
|
+
return command
|
|
973
|
+
delim = m.group(2)
|
|
974
|
+
nl = command.find("\n", m.end())
|
|
975
|
+
if nl == -1:
|
|
976
|
+
return command
|
|
977
|
+
lines = command[nl + 1:].split("\n")
|
|
978
|
+
for i, line in enumerate(lines):
|
|
979
|
+
if line.strip() == delim:
|
|
980
|
+
rest = "\n".join(lines[i + 1:])
|
|
981
|
+
return command[:nl + 1] + _strip_heredoc_bodies(rest)
|
|
982
|
+
# Unterminated heredoc: everything after the marker is body.
|
|
983
|
+
return command[:nl + 1]
|
|
984
|
+
|
|
985
|
+
|
|
986
|
+
def _scan_masked_tokens(command: str) -> List[List[Tuple[str, bool]]]:
|
|
987
|
+
"""Split ``command`` into subcommands of ``(token_text, fully_quoted)``.
|
|
988
|
+
|
|
989
|
+
Quotes/escapes are resolved into the token text (so ``g''h`` -> ``gh``);
|
|
990
|
+
``fully_quoted`` is True when no character of the token was unquoted.
|
|
991
|
+
"""
|
|
992
|
+
subs: List[List[Tuple[str, bool]]] = []
|
|
993
|
+
cur: List[Tuple[str, bool]] = []
|
|
994
|
+
buf: List[str] = []
|
|
995
|
+
has_unquoted = False
|
|
996
|
+
has_any = False
|
|
997
|
+
|
|
998
|
+
def end_token() -> None:
|
|
999
|
+
nonlocal buf, has_unquoted, has_any
|
|
1000
|
+
if has_any:
|
|
1001
|
+
cur.append(("".join(buf), not has_unquoted))
|
|
1002
|
+
buf, has_unquoted, has_any = [], False, False
|
|
1003
|
+
|
|
1004
|
+
def end_sub() -> None:
|
|
1005
|
+
nonlocal cur
|
|
1006
|
+
end_token()
|
|
1007
|
+
if cur:
|
|
1008
|
+
subs.append(cur)
|
|
1009
|
+
cur = []
|
|
1010
|
+
|
|
1011
|
+
i, n = 0, len(command)
|
|
1012
|
+
while i < n:
|
|
1013
|
+
c = command[i]
|
|
1014
|
+
if c == "'":
|
|
1015
|
+
j = command.find("'", i + 1)
|
|
1016
|
+
if j == -1:
|
|
1017
|
+
j = n
|
|
1018
|
+
buf.append(command[i + 1:j])
|
|
1019
|
+
has_any = True
|
|
1020
|
+
i = j + 1
|
|
1021
|
+
elif c == '"':
|
|
1022
|
+
j = i + 1
|
|
1023
|
+
out: List[str] = []
|
|
1024
|
+
while j < n and command[j] != '"':
|
|
1025
|
+
if command[j] == "\\" and j + 1 < n and command[j + 1] in '"\\$`':
|
|
1026
|
+
out.append(command[j + 1])
|
|
1027
|
+
j += 2
|
|
1028
|
+
else:
|
|
1029
|
+
out.append(command[j])
|
|
1030
|
+
j += 1
|
|
1031
|
+
buf.append("".join(out))
|
|
1032
|
+
has_any = True
|
|
1033
|
+
i = j + 1
|
|
1034
|
+
elif c == "\\" and i + 1 < n:
|
|
1035
|
+
buf.append(command[i + 1])
|
|
1036
|
+
has_unquoted = True
|
|
1037
|
+
has_any = True
|
|
1038
|
+
i += 2
|
|
1039
|
+
elif c in " \t":
|
|
1040
|
+
end_token()
|
|
1041
|
+
i += 1
|
|
1042
|
+
elif c in ";\n":
|
|
1043
|
+
end_sub()
|
|
1044
|
+
i += 1
|
|
1045
|
+
elif c in "&|":
|
|
1046
|
+
end_sub()
|
|
1047
|
+
while i < n and command[i] in "&|":
|
|
1048
|
+
i += 1
|
|
1049
|
+
else:
|
|
1050
|
+
buf.append(c)
|
|
1051
|
+
has_unquoted = True
|
|
1052
|
+
has_any = True
|
|
1053
|
+
i += 1
|
|
1054
|
+
end_sub()
|
|
1055
|
+
return subs
|
|
1056
|
+
|
|
1057
|
+
|
|
1058
|
+
def masked_subcommands(command: str) -> List[str]:
|
|
1059
|
+
"""Return per-subcommand strings with content-only quoted spans masked.
|
|
1060
|
+
|
|
1061
|
+
Used for command-prefix (``anchored``) rules so quoted argument text —
|
|
1062
|
+
commit messages, echo strings, heredoc bodies — cannot false-match a
|
|
1063
|
+
command rule, while quoting obfuscation of the command itself still
|
|
1064
|
+
normalizes to the literal form.
|
|
1065
|
+
"""
|
|
1066
|
+
command = _strip_heredoc_bodies(command)
|
|
1067
|
+
|
|
1068
|
+
assigns: Dict[str, str] = {}
|
|
1069
|
+
for m in _ASSIGN_RE.finditer(command):
|
|
1070
|
+
assigns[m.group(1)] = m.group(2).strip("'\"")
|
|
1071
|
+
|
|
1072
|
+
def _resolve(tok: str) -> str:
|
|
1073
|
+
for name, val in assigns.items():
|
|
1074
|
+
tok = tok.replace("${%s}" % name, val).replace("$" + name, val)
|
|
1075
|
+
return tok
|
|
1076
|
+
|
|
1077
|
+
results: List[str] = []
|
|
1078
|
+
for toks in _scan_masked_tokens(command):
|
|
1079
|
+
words: List[str] = []
|
|
1080
|
+
prev = ""
|
|
1081
|
+
for text, fully_quoted in toks:
|
|
1082
|
+
resolved = _resolve(text)
|
|
1083
|
+
is_content = fully_quoted and (not resolved or any(ch in " \t\n" for ch in resolved))
|
|
1084
|
+
if not words and _ASSIGN_TOKEN_RE.match(resolved):
|
|
1085
|
+
# Leading VAR=value assignment — its value is data here (the
|
|
1086
|
+
# assigns table already handles later ``$VAR`` expansion).
|
|
1087
|
+
words.append(_MASK)
|
|
1088
|
+
prev = _MASK
|
|
1089
|
+
continue
|
|
1090
|
+
if is_content:
|
|
1091
|
+
# A ``sh -c "…"`` payload is a command, not content — rescan.
|
|
1092
|
+
if (
|
|
1093
|
+
words
|
|
1094
|
+
and prev.startswith("-")
|
|
1095
|
+
and "c" in prev
|
|
1096
|
+
and words[0].rsplit("/", 1)[-1] in _SHELL_NAMES
|
|
1097
|
+
):
|
|
1098
|
+
results.extend(masked_subcommands(resolved))
|
|
1099
|
+
words.append(_MASK)
|
|
1100
|
+
prev = _MASK
|
|
1101
|
+
else:
|
|
1102
|
+
words.append(resolved)
|
|
1103
|
+
prev = resolved
|
|
1104
|
+
if words and any(w != _MASK for w in words):
|
|
1105
|
+
results.append(" ".join(words))
|
|
1106
|
+
return results
|
|
1107
|
+
|
|
1108
|
+
|
|
921
1109
|
# ============================================================================
|
|
922
1110
|
# DECISION LADDERS
|
|
923
1111
|
# ============================================================================
|
|
@@ -1003,9 +1191,12 @@ def check_command(command: str, config: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
1003
1191
|
# ``ask`` at the end (the unattended resolver turns that into a block).
|
|
1004
1192
|
subcommands, ambiguous = normalize_subcommands(command)
|
|
1005
1193
|
haystacks = [command] + [s for s in subcommands if s and s != command]
|
|
1194
|
+
# Anchored (command-prefix) rules match only masked subcommands, so quoted
|
|
1195
|
+
# argument text — commit messages, echo strings — can't false-match (#675).
|
|
1196
|
+
masked_haystacks = masked_subcommands(command)
|
|
1006
1197
|
|
|
1007
|
-
def _search(pat: str) -> bool:
|
|
1008
|
-
for hay in
|
|
1198
|
+
def _search(pat: str, hays: List[str]) -> bool:
|
|
1199
|
+
for hay in hays:
|
|
1009
1200
|
try:
|
|
1010
1201
|
if re.search(pat, hay, re.IGNORECASE):
|
|
1011
1202
|
return True
|
|
@@ -1025,8 +1216,9 @@ def check_command(command: str, config: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
1025
1216
|
reason = pattern_obj.get("reason", "Matched pattern")
|
|
1026
1217
|
should_ask = pattern_obj.get("ask", False)
|
|
1027
1218
|
bypassable = pattern_obj.get("bypassable", False)
|
|
1219
|
+
anchored = pattern_obj.get("anchored", False)
|
|
1028
1220
|
try:
|
|
1029
|
-
if _search(pattern):
|
|
1221
|
+
if _search(pattern, masked_haystacks if anchored else haystacks):
|
|
1030
1222
|
if should_ask:
|
|
1031
1223
|
return {
|
|
1032
1224
|
"decision": "ask",
|
|
@@ -117,6 +117,25 @@ READ_ONLY_BLOCKED = (
|
|
|
117
117
|
|
|
118
118
|
NO_DELETE_BLOCKED = DELETE_PATTERNS
|
|
119
119
|
|
|
120
|
+
# Interpreter programs can write a file without any shell-level write token:
|
|
121
|
+
# ``python3 -c 'open(p, "w")...'``, a heredoc piped into ``python -``, or
|
|
122
|
+
# ``echo 'open(p,"w")' | python3``. The program text is opaque to static
|
|
123
|
+
# matching, so for PROTECTED control-plane paths we fail closed: an interpreter
|
|
124
|
+
# invocation that takes inline code (-c/-e, combined forms like ``perl -pie``),
|
|
125
|
+
# a heredoc/stdin program, or has data piped into it, AND mentions a protected
|
|
126
|
+
# path anywhere in the command, is blocked (#678). Applied ONLY by
|
|
127
|
+
# ``check_protected_command`` — never to user readOnlyPaths rules — so a
|
|
128
|
+
# legitimate read via ``cat``/``grep`` stays allowed.
|
|
129
|
+
_INTERPRETERS = r'(?:python[\d.]*|perl|ruby|node|deno|bun|php)'
|
|
130
|
+
INTERPRETER_WRITE_PATTERNS = [
|
|
131
|
+
# interpreter ... -c/-e/... or stdin/heredoc marker, path anywhere after
|
|
132
|
+
(r'\b' + _INTERPRETERS +
|
|
133
|
+
r'\b[^\n|;&]*(?:\s-\S*[ceE]\b|\s+-\s|\s+-$|<<)(?s:.*){path}',
|
|
134
|
+
"interpreter program"),
|
|
135
|
+
# path anywhere before a pipe into an interpreter
|
|
136
|
+
(r'{path}(?s:.*)\|\s*' + _INTERPRETERS + r'\b', "pipe into interpreter"),
|
|
137
|
+
]
|
|
138
|
+
|
|
120
139
|
|
|
121
140
|
# ============================================================================
|
|
122
141
|
# GLOB / PATH HELPERS
|
|
@@ -412,7 +431,11 @@ def check_path_patterns(
|
|
|
412
431
|
Returns ``(matched, reason)`` where reason names the operation that triggered.
|
|
413
432
|
"""
|
|
414
433
|
if is_glob_pattern(path):
|
|
415
|
-
|
|
434
|
+
# Allow an optional (possibly quoted) directory prefix: ``*`` in the
|
|
435
|
+
# glob can't span ``/``, so without this a basename glob like
|
|
436
|
+
# ``*.agentwire.yml`` never matched an absolute target
|
|
437
|
+
# (``> /repo/.agentwire.yml`` sailed past every write pattern) (#678).
|
|
438
|
+
path_regex = r'["\']?(?:[^\s"\';|&<>]*/)?' + glob_to_regex(path)
|
|
416
439
|
else:
|
|
417
440
|
expanded = os.path.expanduser(path)
|
|
418
441
|
if expanded == path:
|
|
@@ -480,6 +503,9 @@ def load_write_patterns_from_tooldefs(tooldefs_dir: Optional[Path]) -> List[Dict
|
|
|
480
503
|
"pattern": regex,
|
|
481
504
|
"reason": f"{tool_name}: {cmd_def.get('description', cmd_str)}",
|
|
482
505
|
"ask": True,
|
|
506
|
+
# Command-prefix rule: match command position only, never
|
|
507
|
+
# quoted argument/message content (#675).
|
|
508
|
+
"anchored": True,
|
|
483
509
|
}
|
|
484
510
|
# An explicit ``id:`` on the tooldef command yields a stable
|
|
485
511
|
# rule ID (e.g. ``git.push``) that survives description edits —
|
|
@@ -802,7 +828,10 @@ def check_protected_command(
|
|
|
802
828
|
for path in _protected_path_patterns():
|
|
803
829
|
for hay in haystacks:
|
|
804
830
|
matched, reason = check_path_patterns(
|
|
805
|
-
hay,
|
|
831
|
+
hay,
|
|
832
|
+
path,
|
|
833
|
+
READ_ONLY_BLOCKED + INTERPRETER_WRITE_PATTERNS,
|
|
834
|
+
"protected control-plane path",
|
|
806
835
|
)
|
|
807
836
|
if matched:
|
|
808
837
|
op = _infer_operation_from_reason(reason)
|
|
@@ -914,6 +943,165 @@ def normalize_subcommands(command: str) -> Tuple[List[str], Optional[str]]:
|
|
|
914
943
|
return subs, None
|
|
915
944
|
|
|
916
945
|
|
|
946
|
+
#
|
|
947
|
+
# CONTENT MASKING (#675)
|
|
948
|
+
#
|
|
949
|
+
# Tooldef rules are COMMAND-PREFIX rules (``\bgh\s+pr\s+comment\b``), but the
|
|
950
|
+
# haystacks above include quoted argument text — so a commit message that
|
|
951
|
+
# merely *mentions* "gh pr comment" false-matches the rule. Anchored rules are
|
|
952
|
+
# therefore matched against MASKED subcommands instead: quoted spans that can
|
|
953
|
+
# only be content (a quoted token containing whitespace — a message, an echo
|
|
954
|
+
# string, a heredoc body) are replaced with a placeholder, while quoted
|
|
955
|
+
# single words are kept (``"gh" pr comment`` is still a real command) and
|
|
956
|
+
# ``sh -c "…"`` payloads are recursively re-scanned as commands.
|
|
957
|
+
|
|
958
|
+
_MASK = "\x00"
|
|
959
|
+
_HEREDOC_RE = re.compile(r"<<-?\s*(['\"]?)([A-Za-z_]\w+)\1")
|
|
960
|
+
_SHELL_NAMES = {"sh", "bash", "zsh", "dash", "ksh"}
|
|
961
|
+
_ASSIGN_TOKEN_RE = re.compile(r"^[A-Za-z_]\w*=")
|
|
962
|
+
|
|
963
|
+
|
|
964
|
+
def _strip_heredoc_bodies(command: str) -> str:
|
|
965
|
+
"""Remove heredoc body lines (``<<EOF … EOF``) — they are content."""
|
|
966
|
+
m = _HEREDOC_RE.search(command)
|
|
967
|
+
if not m:
|
|
968
|
+
return command
|
|
969
|
+
delim = m.group(2)
|
|
970
|
+
nl = command.find("\n", m.end())
|
|
971
|
+
if nl == -1:
|
|
972
|
+
return command
|
|
973
|
+
lines = command[nl + 1:].split("\n")
|
|
974
|
+
for i, line in enumerate(lines):
|
|
975
|
+
if line.strip() == delim:
|
|
976
|
+
rest = "\n".join(lines[i + 1:])
|
|
977
|
+
return command[:nl + 1] + _strip_heredoc_bodies(rest)
|
|
978
|
+
# Unterminated heredoc: everything after the marker is body.
|
|
979
|
+
return command[:nl + 1]
|
|
980
|
+
|
|
981
|
+
|
|
982
|
+
def _scan_masked_tokens(command: str) -> List[List[Tuple[str, bool]]]:
|
|
983
|
+
"""Split ``command`` into subcommands of ``(token_text, fully_quoted)``.
|
|
984
|
+
|
|
985
|
+
Quotes/escapes are resolved into the token text (so ``g''h`` -> ``gh``);
|
|
986
|
+
``fully_quoted`` is True when no character of the token was unquoted.
|
|
987
|
+
"""
|
|
988
|
+
subs: List[List[Tuple[str, bool]]] = []
|
|
989
|
+
cur: List[Tuple[str, bool]] = []
|
|
990
|
+
buf: List[str] = []
|
|
991
|
+
has_unquoted = False
|
|
992
|
+
has_any = False
|
|
993
|
+
|
|
994
|
+
def end_token() -> None:
|
|
995
|
+
nonlocal buf, has_unquoted, has_any
|
|
996
|
+
if has_any:
|
|
997
|
+
cur.append(("".join(buf), not has_unquoted))
|
|
998
|
+
buf, has_unquoted, has_any = [], False, False
|
|
999
|
+
|
|
1000
|
+
def end_sub() -> None:
|
|
1001
|
+
nonlocal cur
|
|
1002
|
+
end_token()
|
|
1003
|
+
if cur:
|
|
1004
|
+
subs.append(cur)
|
|
1005
|
+
cur = []
|
|
1006
|
+
|
|
1007
|
+
i, n = 0, len(command)
|
|
1008
|
+
while i < n:
|
|
1009
|
+
c = command[i]
|
|
1010
|
+
if c == "'":
|
|
1011
|
+
j = command.find("'", i + 1)
|
|
1012
|
+
if j == -1:
|
|
1013
|
+
j = n
|
|
1014
|
+
buf.append(command[i + 1:j])
|
|
1015
|
+
has_any = True
|
|
1016
|
+
i = j + 1
|
|
1017
|
+
elif c == '"':
|
|
1018
|
+
j = i + 1
|
|
1019
|
+
out: List[str] = []
|
|
1020
|
+
while j < n and command[j] != '"':
|
|
1021
|
+
if command[j] == "\\" and j + 1 < n and command[j + 1] in '"\\$`':
|
|
1022
|
+
out.append(command[j + 1])
|
|
1023
|
+
j += 2
|
|
1024
|
+
else:
|
|
1025
|
+
out.append(command[j])
|
|
1026
|
+
j += 1
|
|
1027
|
+
buf.append("".join(out))
|
|
1028
|
+
has_any = True
|
|
1029
|
+
i = j + 1
|
|
1030
|
+
elif c == "\\" and i + 1 < n:
|
|
1031
|
+
buf.append(command[i + 1])
|
|
1032
|
+
has_unquoted = True
|
|
1033
|
+
has_any = True
|
|
1034
|
+
i += 2
|
|
1035
|
+
elif c in " \t":
|
|
1036
|
+
end_token()
|
|
1037
|
+
i += 1
|
|
1038
|
+
elif c in ";\n":
|
|
1039
|
+
end_sub()
|
|
1040
|
+
i += 1
|
|
1041
|
+
elif c in "&|":
|
|
1042
|
+
end_sub()
|
|
1043
|
+
while i < n and command[i] in "&|":
|
|
1044
|
+
i += 1
|
|
1045
|
+
else:
|
|
1046
|
+
buf.append(c)
|
|
1047
|
+
has_unquoted = True
|
|
1048
|
+
has_any = True
|
|
1049
|
+
i += 1
|
|
1050
|
+
end_sub()
|
|
1051
|
+
return subs
|
|
1052
|
+
|
|
1053
|
+
|
|
1054
|
+
def masked_subcommands(command: str) -> List[str]:
|
|
1055
|
+
"""Return per-subcommand strings with content-only quoted spans masked.
|
|
1056
|
+
|
|
1057
|
+
Used for command-prefix (``anchored``) rules so quoted argument text —
|
|
1058
|
+
commit messages, echo strings, heredoc bodies — cannot false-match a
|
|
1059
|
+
command rule, while quoting obfuscation of the command itself still
|
|
1060
|
+
normalizes to the literal form.
|
|
1061
|
+
"""
|
|
1062
|
+
command = _strip_heredoc_bodies(command)
|
|
1063
|
+
|
|
1064
|
+
assigns: Dict[str, str] = {}
|
|
1065
|
+
for m in _ASSIGN_RE.finditer(command):
|
|
1066
|
+
assigns[m.group(1)] = m.group(2).strip("'\"")
|
|
1067
|
+
|
|
1068
|
+
def _resolve(tok: str) -> str:
|
|
1069
|
+
for name, val in assigns.items():
|
|
1070
|
+
tok = tok.replace("${%s}" % name, val).replace("$" + name, val)
|
|
1071
|
+
return tok
|
|
1072
|
+
|
|
1073
|
+
results: List[str] = []
|
|
1074
|
+
for toks in _scan_masked_tokens(command):
|
|
1075
|
+
words: List[str] = []
|
|
1076
|
+
prev = ""
|
|
1077
|
+
for text, fully_quoted in toks:
|
|
1078
|
+
resolved = _resolve(text)
|
|
1079
|
+
is_content = fully_quoted and (not resolved or any(ch in " \t\n" for ch in resolved))
|
|
1080
|
+
if not words and _ASSIGN_TOKEN_RE.match(resolved):
|
|
1081
|
+
# Leading VAR=value assignment — its value is data here (the
|
|
1082
|
+
# assigns table already handles later ``$VAR`` expansion).
|
|
1083
|
+
words.append(_MASK)
|
|
1084
|
+
prev = _MASK
|
|
1085
|
+
continue
|
|
1086
|
+
if is_content:
|
|
1087
|
+
# A ``sh -c "…"`` payload is a command, not content — rescan.
|
|
1088
|
+
if (
|
|
1089
|
+
words
|
|
1090
|
+
and prev.startswith("-")
|
|
1091
|
+
and "c" in prev
|
|
1092
|
+
and words[0].rsplit("/", 1)[-1] in _SHELL_NAMES
|
|
1093
|
+
):
|
|
1094
|
+
results.extend(masked_subcommands(resolved))
|
|
1095
|
+
words.append(_MASK)
|
|
1096
|
+
prev = _MASK
|
|
1097
|
+
else:
|
|
1098
|
+
words.append(resolved)
|
|
1099
|
+
prev = resolved
|
|
1100
|
+
if words and any(w != _MASK for w in words):
|
|
1101
|
+
results.append(" ".join(words))
|
|
1102
|
+
return results
|
|
1103
|
+
|
|
1104
|
+
|
|
917
1105
|
# ============================================================================
|
|
918
1106
|
# DECISION LADDERS
|
|
919
1107
|
# ============================================================================
|
|
@@ -999,9 +1187,12 @@ def check_command(command: str, config: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
999
1187
|
# ``ask`` at the end (the unattended resolver turns that into a block).
|
|
1000
1188
|
subcommands, ambiguous = normalize_subcommands(command)
|
|
1001
1189
|
haystacks = [command] + [s for s in subcommands if s and s != command]
|
|
1190
|
+
# Anchored (command-prefix) rules match only masked subcommands, so quoted
|
|
1191
|
+
# argument text — commit messages, echo strings — can't false-match (#675).
|
|
1192
|
+
masked_haystacks = masked_subcommands(command)
|
|
1002
1193
|
|
|
1003
|
-
def _search(pat: str) -> bool:
|
|
1004
|
-
for hay in
|
|
1194
|
+
def _search(pat: str, hays: List[str]) -> bool:
|
|
1195
|
+
for hay in hays:
|
|
1005
1196
|
try:
|
|
1006
1197
|
if re.search(pat, hay, re.IGNORECASE):
|
|
1007
1198
|
return True
|
|
@@ -1021,8 +1212,9 @@ def check_command(command: str, config: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
1021
1212
|
reason = pattern_obj.get("reason", "Matched pattern")
|
|
1022
1213
|
should_ask = pattern_obj.get("ask", False)
|
|
1023
1214
|
bypassable = pattern_obj.get("bypassable", False)
|
|
1215
|
+
anchored = pattern_obj.get("anchored", False)
|
|
1024
1216
|
try:
|
|
1025
|
-
if _search(pattern):
|
|
1217
|
+
if _search(pattern, masked_haystacks if anchored else haystacks):
|
|
1026
1218
|
if should_ask:
|
|
1027
1219
|
return {
|
|
1028
1220
|
"decision": "ask",
|