agentwire-dev 1.29.0__tar.gz → 1.29.2__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.29.0 → agentwire_dev-1.29.2}/CHANGELOG.md +1 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/PKG-INFO +2 -1
- agentwire_dev-1.29.2/SECURITY.md +59 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/__init__.py +1 -1
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/config.py +5 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/server.py +94 -36
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/css/desktop.css +273 -0
- agentwire_dev-1.29.2/agentwire/static/js/command-palette.js +601 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/js/desktop-manager.js +14 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/js/desktop.js +55 -7
- agentwire_dev-1.29.2/agentwire/static/js/pinned-message.js +232 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/js/sidebar/projects-section.js +3 -3
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/js/windows/chat-window.js +7 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/templates/desktop.html +1 -1
- agentwire_dev-1.29.2/docs/decisions/obsidian-second-brain.md +88 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/docs/wiki/INDEX.md +3 -2
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/docs/wiki/deployment/remote-machines.md +3 -3
- agentwire_dev-1.29.2/docs/wiki/tts/stt-self-hosted.md +108 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/pyproject.toml +2 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/conftest.py +1 -1
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_cli_safety.py +6 -2
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_history.py +2 -2
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_missions_config.py +5 -5
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_project_config.py +4 -4
- agentwire_dev-1.29.0/SECURITY.md +0 -46
- agentwire_dev-1.29.0/agentwire/static/js/quicktask-modal.js +0 -267
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/.github/FUNDING.yml +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/.github/ISSUE_TEMPLATE/question.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/.gitignore +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/CLA.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/CODE_OF_CONDUCT.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/CONTRIBUTING.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/Dockerfile.local +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/Dockerfile.runpod +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/LICENSE +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/README.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/RELEASING.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/SPONSORS.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/__main__.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/agents/__init__.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/agents/base.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/agents/tmux.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/cached_status.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/channels/__init__.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/channels/base.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/channels/email.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/channels/quo.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/cli_safety.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/completion.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/fetch.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/handoff/__init__.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/handoff/git_state.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/handoff/instructions.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/handoff/parser.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/handoff/renderer.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/handoff/schema.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/history.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/hooks/__init__.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/hooks/agentwire-permission.sh +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/hooks/damage-control/__init__.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/hooks/damage-control/audit_logger.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/hooks/damage-control/bash-tool-damage-control.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/hooks/damage-control/edit-tool-damage-control.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/hooks/damage-control/rules/agentwire.yaml +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/hooks/damage-control/rules/aws.yaml +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/hooks/damage-control/rules/cloud-hosting.yaml +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/hooks/damage-control/rules/containers.yaml +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/hooks/damage-control/rules/core.yaml +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/hooks/damage-control/rules/databases.yaml +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/hooks/damage-control/rules/firebase.yaml +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/hooks/damage-control/rules/gcp.yaml +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/hooks/damage-control/rules/git.yaml +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/hooks/damage-control/rules/gws.yaml +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/hooks/damage-control/rules/infrastructure.yaml +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/hooks/damage-control/rules/remote.yaml +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/hooks/damage-control/write-tool-damage-control.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/hooks/idle-handler.sh +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/listen.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/locking.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/mcp_server.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/missions/__init__.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/missions/cli.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/missions/config.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/missions/dispatcher.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/missions/eligibility.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/missions/feedback_router.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/missions/gc.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/missions/github.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/missions/naming.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/missions/state.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/network.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/onboarding.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/overnight.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/pane_manager.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/project_config.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/projects.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/prompts/__init__.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/prompts/init.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/roles/__init__.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/roles/agentwire.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/roles/chatbot.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/roles/init.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/roles/notifications.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/roles/orchestrator.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/roles/task-runner.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/roles/voice.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/roles/worker.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/safety/__init__.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/safety/_core.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/scheduler.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/search.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/agentwire-Echo--black.png +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/agentwire-Echo--transparent.png +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/agentwire-Echo.png +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/agentwire-email-banner.png +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/agentwire-splash-logo-layers--agentwire-text.png +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/agentwire-splash-logo-layers--echo-claw-fg.png +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/agentwire-splash-logo-layers--echo.png +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/agentwire-splash-logo-layers--full--transparent-top.png +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/agentwire-splash-logo-layers--full-black.png +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/agentwire-splash-logo-layers--telephone-fg.png +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/agentwire-splash-logo-layers--telephone.png +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/agentwire-splash-logo-layers--transparent-top.png +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/agentwire-splash-logo-layers--transparent.png +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/agentwire-splash-logo-layers--tree.png +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/agentwire-splash-logo-layers.png +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/favicon.png +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/machines/android.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/machines/automaton.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/machines/bot.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/machines/cyborg.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/machines/droid.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/machines/drone.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/machines/guardian.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/machines/mech.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/machines/probe.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/machines/robot.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/machines/sentinel.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/machines/unit.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/projects/blob.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/projects/cloud.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/projects/crystal.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/projects/cyclops.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/projects/flame.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/projects/fuzzy.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/projects/horned.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/projects/moon.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/projects/slime.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/projects/star.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/projects/tentacle.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/projects/winged.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/sessions/bear.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/sessions/cat.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/sessions/crown.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/sessions/custom/agentwire-portal.png +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/sessions/custom/agentwire-tts.png +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/sessions/custom/agentwire.png +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/sessions/deer.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/sessions/drone.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/sessions/eagle.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/sessions/fox.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/sessions/hawk.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/sessions/horse.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/sessions/lion.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/sessions/rabbit.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/sessions/robot.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/sessions/tiger.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/icons/sessions/wolf.jpeg +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/js/.gitkeep +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/js/artifact-window.js +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/js/components/icon-picker.js +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/js/components/list-card.js +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/js/components/type-tag.js +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/js/icon-manager.js +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/js/new-project-modal.js +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/js/notifications-panel.js +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/js/safety-shared.js +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/js/safety-window.js +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/js/session-id.js +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/js/session-window.js +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/js/sidebar/artifacts-section.js +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/js/sidebar/config-section.js +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/js/sidebar/machines-section.js +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/js/sidebar/missions-section.js +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/js/sidebar/safety-section.js +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/js/sidebar/scheduler-section.js +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/js/sidebar/services-section.js +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/js/sidebar/sessions-section.js +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/js/sidebar.js +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/js/terminal-font-prefs.js +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/js/tile-manager.js +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/js/utils/auto-refresh.js +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/static/js/winbox.bundle.min.js +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/stt/__init__.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/stt/base.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/stt/server_backend.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/stt/stt_server.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/stt/whisperkit.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/tasks.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/templates/__init__.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/templates/base.html +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/templates/email_notification.html +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/templates/handoff/show-the-story.html.j2 +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/templates/handoff/theme.css.j2 +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/templates/tmux.conf +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/templating.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/tooldefs/aws.yaml +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/tooldefs/docker.yaml +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/tooldefs/gcp.yaml +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/tooldefs/gh.yaml +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/tooldefs/git.yaml +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/tooldefs/gws.yaml +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/tooldefs/kubectl.yaml +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/tooldefs/npm.yaml +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/tooldefs/terraform.yaml +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/tooldefs/uv.yaml +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/tts/__init__.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/tts/base.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/tts/engines/__init__.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/tts/engines/chatterbox.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/tts/engines/kokoro.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/tts/engines/qwen_base.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/tts/engines/qwen_custom.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/tts/engines/qwen_design.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/tts/engines/zonos.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/tts/registry.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/tts/runpod_handler.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/tts_server.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/tunnels.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/utils/__init__.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/utils/chunker.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/utils/file_io.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/utils/paths.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/utils/subprocess.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/validation.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/voiceclone.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/voices/darren.wav +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/voices/default.wav +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/voices/jessica.wav +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/voices/lisa.wav +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/voices/may.wav +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/agentwire/worktree.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/docs/logo.png +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/docs/wiki/architecture.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/docs/wiki/communication/channels.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/docs/wiki/communication/hammerspoon.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/docs/wiki/communication/handoff.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/docs/wiki/concepts.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/docs/wiki/deployment/remote-access.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/docs/wiki/glossary.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/docs/wiki/integrations/gws-google-workspace-cli.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/docs/wiki/internals/damage-control.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/docs/wiki/internals/portal.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/docs/wiki/internals/shell-escaping.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/docs/wiki/internals/troubleshooting.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/docs/wiki/missions.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/docs/wiki/quickstart.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/docs/wiki/scheduling/scheduled-workloads.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/docs/wiki/sessions/claude-code-auto-mode.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/docs/wiki/sessions/pi.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/docs/wiki/tts/runpod-tts.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/docs/wiki/tts/tts-self-hosted.md +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/requirements-tts.txt +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/templates/launchd/dev.agentwire.mission-dispatcher.plist +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/templates/launchd/dev.agentwire.mission-feedback-router.plist +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/templates/launchd/dev.agentwire.mission-janitor.plist +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/e2e/test_portal_ui.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/fixtures/sample_agentwire.yml +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/fixtures/sample_config.yaml +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/fixtures/sample_scheduler.yaml +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/integration/test_missions_concurrency.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/integration/test_missions_lifecycle.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/integration/test_scheduler_board.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/integration/test_server_websockets.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_build_agent_command.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_channels.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_cli_commands.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_cli_output.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_config.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_damage_control_hooks.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_damage_control_sync.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_file_io.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_handoff_git_state.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_handoff_instructions.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_handoff_parser.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_handoff_renderer.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_locking.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_mcp_server.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_mcp_tools_args.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_missions_cli.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_missions_dispatcher.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_missions_eligibility.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_missions_feedback_router.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_missions_gc.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_missions_github.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_missions_naming.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_missions_state.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_overnight_resume_flags.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_portal_api.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_roles.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_safety_disabled_rules.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_safety_escape_hatch.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_safety_kill_switch.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_safety_mission_worker.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_scheduler.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_scheduler_parsing.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_search.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_server_async.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_server_pure.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_tasks.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_templating.py +0 -0
- {agentwire_dev-1.29.0 → agentwire_dev-1.29.2}/tests/unit/test_worktree.py +0 -0
|
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
10
10
|
### Removed
|
|
11
11
|
|
|
12
12
|
- **Workflow engine** — entire `agentwire/workflows/` package (DAG runner, definitions, storage, pi runner, CLI), the `agentwire workflow` CLI subcommand, 5 MCP tools (`workflow_list/validate/run/history/show`), 3 portal endpoints (`/api/workflows/list|runs|runs/{id}`), the Workflows sidebar section + WorkflowWindow + ~230 lines of CSS, the scheduler's `workflow:` dispatch branch (`SchedulerTask.workflow`/`inputs` fields, `_dispatch_workflow_task`, `_render_workflow_inputs`, `_parse_workflow_summary`, `_workflow_failure_state`, validation block), 9 workflow test files plus the workflow-specific cases in `test_scheduler.py`, the `agentwire-workflows` skill, `docs/wiki/scheduling/workflows.md`, and ~120 dirs of historical runs under `~/.agentwire/workflows/`. The user's actual recurring automations all live as scheduler ensure tasks (`task:`); the engine was unused in practice and the empty sidebar surface was just noise. Scheduler is now ensure-only.
|
|
13
|
+
- **On-hold mission docs** — retroactive accounting for the seven on-hold files left behind by #153 and wholesale-deleted in commit `2425847` (2026-05-13) as part of the GitHub-issues-as-SSOT migration: `docs/missions/OBJECTIVE.md` (folded into README + website), `docs/missions/pi-harness-overview.md` (roadmap superseded — phases shipped or moved to research wiki), `docs/missions/pi-workflow-advanced.md` (on-hold-awaiting-usage stance has no scoped plan to track), `docs/missions/pi-workflow-ui.md` (visible portion shipped; speculative remainder unscoped), `docs/missions/later/multi-channel-bus.md` (premise actively rejected — wire surface is now outbound-only), `docs/missions/later/personal-replica.md` (speculative ideation, not a plan), `docs/missions/later/ralph-loop-use-cases.md` (brainstorm artifact). Closes [#154](https://github.com/dotdevdotdev/agentwire-dev/issues/154).
|
|
13
14
|
- **Inbound channel surface** — entire Telegram bridge (`agentwire/bridges/telegram.py`), Discord channel (`agentwire/channels/discord.py`), Slack channel (`agentwire/channels/slack.py`), Twilio SMS (`agentwire/channels/sms.py`), Webhook (`agentwire/channels/webhook.py`), outbound-Telegram (`agentwire/channels/telegram.py`), channel scaffolding template (`agentwire/channels/_template.py`), all `agentwire {telegram,discord,slack,sms,webhook}` CLI command groups, the `agentwire reply` command, MCP tools `discord_status` / `slack_status` / `sms_send` / `webhook_send`, the portal sidebar "socials" section, the `ServiceChannel` / `MessageQueueManager` / `QueuedMessage` / `compose_session_config` / `inject_instructions` / session-helper plumbing in `channels/base.py`, the TTS/STT primitives on the channel base class, the auto-injected `discord-dm` / `slack-dm` / `channel-admin` roles, the `OutputConfig.notify` task field plus its `_handle_task_notification` dispatcher (voice / alert / webhook / email / command), the doctor's Telegram bridge check, and the `aiogram` optional dep. Wire surface is now outbound-only: **email** (Resend) and **quo** (OpenPhone SMS) remain. Inbound user input flows through the portal. Security review traced the cut: Telegram bridge was secure (refuse-to-start on empty allowlist + silent default-deny), Discord/Slack fail-open on empty allowlist + unauthenticated channel `@mention`; rather than fix the gaps, the whole inbound surface comes out to keep the wire posture outbound-only.
|
|
14
15
|
- **Anthropic SDK surface** — entire `agentwire/sdk/` package, `agentwire/repl/` (Textual REPL), `agentwire/workflows/runners/{anthropic,human_gate}.py`, `sdk-bypass` / `sdk-prompted` / `sdk-restricted` session types, `sdk-watch` portal window + sidebar section, `--runner` workflow CLI override, and the `claude-agent-sdk` dependency. Anthropic moved Agent SDK + `claude -p` usage out of the monthly subscription tier on 2026-05-13 (now API-billed); the in-tree consumers were too expensive to keep running. The `pi` workflow runner is now the only runner. For Anthropic-quality scheduled work, use a `claude-bypass` tmux session via `.agentwire.yml` + scheduler. Mission [#184](https://github.com/dotdevdotdev/agentwire-dev/issues/184). Findings captured in `~/.agentwire/wiki/` (anthropic-agent-sdk, anthropic-runner-findings, textual-repl-retrospective, workflow-runner-choice, claude-code-print-mode).
|
|
15
16
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentwire-dev
|
|
3
|
-
Version: 1.29.
|
|
3
|
+
Version: 1.29.2
|
|
4
4
|
Summary: Multi-session voice web interface for AI coding agents
|
|
5
5
|
Project-URL: Homepage, https://agentwire.dev
|
|
6
6
|
Project-URL: Repository, https://github.com/dotdevdotdev/agentwire-dev
|
|
@@ -686,6 +686,7 @@ Classifier: Topic :: Software Development :: User Interfaces
|
|
|
686
686
|
Requires-Python: >=3.10
|
|
687
687
|
Requires-Dist: aiohttp-jinja2>=1.6
|
|
688
688
|
Requires-Dist: aiohttp>=3.9.0
|
|
689
|
+
Requires-Dist: av>=12.0.0
|
|
689
690
|
Requires-Dist: jinja2>=3.1.0
|
|
690
691
|
Requires-Dist: markdown>=3.5
|
|
691
692
|
Requires-Dist: mcp>=1.2.0
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Reporting a Vulnerability
|
|
4
|
+
|
|
5
|
+
If you discover a security vulnerability in AgentWire, please report it privately.
|
|
6
|
+
|
|
7
|
+
**Do NOT open a public GitHub issue for security vulnerabilities.**
|
|
8
|
+
|
|
9
|
+
### How to Report
|
|
10
|
+
|
|
11
|
+
Email: security@agentwire.dev
|
|
12
|
+
|
|
13
|
+
Include:
|
|
14
|
+
- Description of the vulnerability
|
|
15
|
+
- Steps to reproduce
|
|
16
|
+
- Potential impact
|
|
17
|
+
- Any suggested fixes (optional)
|
|
18
|
+
|
|
19
|
+
### What to Expect
|
|
20
|
+
|
|
21
|
+
- **Acknowledgment:** Within 48 hours
|
|
22
|
+
- **Initial Assessment:** Within 1 week
|
|
23
|
+
- **Resolution Timeline:** Depends on severity, typically 30-90 days
|
|
24
|
+
|
|
25
|
+
### Scope
|
|
26
|
+
|
|
27
|
+
This security policy applies to:
|
|
28
|
+
- The AgentWire CLI (`agentwire` command)
|
|
29
|
+
- The AgentWire portal (web interface)
|
|
30
|
+
- Official AgentWire packages on PyPI
|
|
31
|
+
|
|
32
|
+
### Out of Scope
|
|
33
|
+
|
|
34
|
+
- Third-party dependencies (report to their maintainers)
|
|
35
|
+
- Self-hosted TTS/STT servers
|
|
36
|
+
- User misconfiguration
|
|
37
|
+
|
|
38
|
+
## Security Features
|
|
39
|
+
|
|
40
|
+
AgentWire includes built-in security features:
|
|
41
|
+
|
|
42
|
+
- **Damage Control Hooks:** Block 300+ dangerous command patterns
|
|
43
|
+
- **Path Protection:** Prevent access to sensitive files (.env, SSH keys, credentials)
|
|
44
|
+
- **Audit Logging:** All blocked operations are logged
|
|
45
|
+
|
|
46
|
+
See `docs/wiki/internals/damage-control.md` for details.
|
|
47
|
+
|
|
48
|
+
## Trust Model
|
|
49
|
+
|
|
50
|
+
The portal (HTTPS server bound to `0.0.0.0:8765` by default) **has no built-in authentication or authorization on its API endpoints**. This is by design — agentwire is built for a local-network trust perimeter, typically running on the same machine as the operator's browser or behind a Cloudflare Tunnel + Zero Trust gate (see `docs/wiki/deployment/remote-access.md`).
|
|
51
|
+
|
|
52
|
+
What this means in practice:
|
|
53
|
+
|
|
54
|
+
- **Anyone who can reach the portal port can drive it.** All `/api/*` endpoints (scheduler control, missions dispatch, project deletion, artifact upload, desktop window control) execute without auth.
|
|
55
|
+
- **Do not expose the portal directly to the public internet.** Use either firewall rules limiting access to trusted IPs, or front it with an auth gateway. Cloudflare Tunnel + Zero Trust is the recommended pattern; details in the deployment docs.
|
|
56
|
+
- **Project deletion via `/api/projects/delete`** validates the path is absolute, contains no `..`, contains no shell metacharacters, and is not in a protected list. Local execution uses argv form (no shell); SSH execution uses `shlex.quote` per argument. These mitigations don't substitute for perimeter security — they reduce blast radius if the perimeter fails.
|
|
57
|
+
- **CSRF / Origin checks are not enforced** on state-changing POSTs. A browser inside the trust perimeter that loads attacker-controlled content could be coerced into making requests to the portal. If your portal is reachable from a browser on a less-trusted network, add an origin check or fence it behind an auth proxy.
|
|
58
|
+
|
|
59
|
+
If you need authentication, the recommended path is **Cloudflare Tunnel + Zero Trust** rather than adding auth in-process: identity, MFA, audit, and revocation are all handled upstream and survive process restarts.
|
|
@@ -109,6 +109,10 @@ class STTConfig:
|
|
|
109
109
|
|
|
110
110
|
url: str | None = None # STT server URL (e.g., http://localhost:8100)
|
|
111
111
|
timeout: int = 30
|
|
112
|
+
# Milliseconds of silence to prepend to the decoded audio before sending
|
|
113
|
+
# to the STT backend. Default 0 — moonshine doesn't need it. Set to ~300
|
|
114
|
+
# if your backend (e.g. older faster-whisper builds) clips the first syllable.
|
|
115
|
+
silence_prepend_ms: int = 0
|
|
112
116
|
|
|
113
117
|
|
|
114
118
|
@dataclass
|
|
@@ -406,6 +410,7 @@ def _dict_to_config(data: dict) -> Config:
|
|
|
406
410
|
stt = STTConfig(
|
|
407
411
|
url=stt_data.get("url"),
|
|
408
412
|
timeout=stt_data.get("timeout", 30),
|
|
413
|
+
silence_prepend_ms=int(stt_data.get("silence_prepend_ms", 0)),
|
|
409
414
|
)
|
|
410
415
|
|
|
411
416
|
# Agent
|
|
@@ -2343,39 +2343,49 @@ class AgentWireServer:
|
|
|
2343
2343
|
machine = data.get("machine")
|
|
2344
2344
|
delete_type = data.get("deleteType")
|
|
2345
2345
|
|
|
2346
|
-
if not path:
|
|
2346
|
+
if not path or not isinstance(path, str):
|
|
2347
2347
|
return web.json_response({"success": False, "error": "Missing path"})
|
|
2348
2348
|
if delete_type not in ("config", "folder"):
|
|
2349
2349
|
return web.json_response({"success": False, "error": "Invalid deleteType"})
|
|
2350
2350
|
|
|
2351
|
-
#
|
|
2351
|
+
# Path validation: absolute, no traversal, no shell metacharacters.
|
|
2352
|
+
# The endpoint has no auth (local-trust model — see SECURITY.md), so
|
|
2353
|
+
# treat the input as untrusted regardless and reject anything that
|
|
2354
|
+
# could escape argv quoting on either local or remote (SSH) execution.
|
|
2355
|
+
if not path.startswith("/"):
|
|
2356
|
+
return web.json_response({"success": False, "error": "path must be absolute"})
|
|
2357
|
+
if ".." in Path(path).parts:
|
|
2358
|
+
return web.json_response({"success": False, "error": "path may not contain '..'"})
|
|
2359
|
+
if re.search(r"[\s;&|`$<>(){}\[\]\\\"'*?#]", path):
|
|
2360
|
+
return web.json_response({"success": False, "error": "path contains disallowed characters"})
|
|
2361
|
+
if path.rstrip("/") in ("", "/root", "/home", "/Users", "/tmp", "/etc") or path.rstrip("/") in ("~", "$HOME"):
|
|
2362
|
+
return web.json_response({"success": False, "error": "Cannot delete protected paths"})
|
|
2363
|
+
|
|
2364
|
+
# Build argv. For SSH we still need to cross a remote shell, so
|
|
2365
|
+
# quote with shlex; locally we use array form with shell=False.
|
|
2352
2366
|
if delete_type == "config":
|
|
2353
|
-
|
|
2367
|
+
target = f"{path.rstrip('/')}/.agentwire.yml"
|
|
2368
|
+
local_argv = ["rm", "-f", target]
|
|
2354
2369
|
else:
|
|
2355
|
-
|
|
2356
|
-
if path in ("/", "/root", "/home") or path.rstrip("/") in ("~", "$HOME"):
|
|
2357
|
-
return web.json_response({"success": False, "error": "Cannot delete protected paths"})
|
|
2358
|
-
cmd = f"rm -rf '{path}'"
|
|
2370
|
+
local_argv = ["rm", "-rf", path]
|
|
2359
2371
|
|
|
2360
|
-
# Execute locally or remotely
|
|
2361
2372
|
if machine and machine != "local":
|
|
2362
|
-
# Remote
|
|
2373
|
+
# Remote shell — quote each argv element through shlex.
|
|
2374
|
+
remote_cmd = " ".join(shlex.quote(a) for a in local_argv)
|
|
2363
2375
|
result = await asyncio.to_thread(
|
|
2364
2376
|
subprocess.run,
|
|
2365
|
-
["ssh", machine,
|
|
2377
|
+
["ssh", machine, remote_cmd],
|
|
2366
2378
|
capture_output=True,
|
|
2367
2379
|
text=True,
|
|
2368
|
-
timeout=30
|
|
2380
|
+
timeout=30,
|
|
2369
2381
|
)
|
|
2370
2382
|
else:
|
|
2371
|
-
# Local
|
|
2372
2383
|
result = await asyncio.to_thread(
|
|
2373
2384
|
subprocess.run,
|
|
2374
|
-
|
|
2375
|
-
shell=True,
|
|
2385
|
+
local_argv,
|
|
2376
2386
|
capture_output=True,
|
|
2377
2387
|
text=True,
|
|
2378
|
-
timeout=30
|
|
2388
|
+
timeout=30,
|
|
2379
2389
|
)
|
|
2380
2390
|
|
|
2381
2391
|
if result.returncode != 0:
|
|
@@ -3273,7 +3283,13 @@ projects:
|
|
|
3273
3283
|
return web.json_response({"error": str(e)}, status=500)
|
|
3274
3284
|
|
|
3275
3285
|
async def handle_transcribe(self, request: web.Request) -> web.Response:
|
|
3276
|
-
"""Transcribe audio to text.
|
|
3286
|
+
"""Transcribe audio to text.
|
|
3287
|
+
|
|
3288
|
+
Decodes WebM/Opus uploads in-process via PyAV (no ffmpeg subprocess
|
|
3289
|
+
startup) and resamples to 16 kHz mono PCM16 — the canonical input
|
|
3290
|
+
shape for Whisper- and Moonshine-class models. Optionally prepends a
|
|
3291
|
+
configurable amount of silence (``stt.silence_prepend_ms``, default 0).
|
|
3292
|
+
"""
|
|
3277
3293
|
try:
|
|
3278
3294
|
reader = await request.multipart()
|
|
3279
3295
|
audio_field = await reader.next()
|
|
@@ -3281,46 +3297,88 @@ projects:
|
|
|
3281
3297
|
if audio_field is None:
|
|
3282
3298
|
return web.json_response({"error": "No audio data"})
|
|
3283
3299
|
|
|
3284
|
-
# Read audio data
|
|
3285
3300
|
audio_data = await audio_field.read()
|
|
3286
|
-
|
|
3287
3301
|
if not audio_data:
|
|
3288
3302
|
return web.json_response({"error": "Empty audio data"})
|
|
3289
3303
|
|
|
3290
|
-
|
|
3291
|
-
with tempfile.NamedTemporaryFile(suffix=".webm", delete=False) as f:
|
|
3292
|
-
f.write(audio_data)
|
|
3293
|
-
webm_path = f.name
|
|
3304
|
+
silence_ms = int(getattr(self.config.stt, "silence_prepend_ms", 0) or 0)
|
|
3294
3305
|
|
|
3295
|
-
# Convert webm to wav (16kHz mono for Whisper)
|
|
3296
|
-
wav_path = webm_path.replace(".webm", ".wav")
|
|
3297
3306
|
try:
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
stderr=asyncio.subprocess.DEVNULL,
|
|
3307
|
+
wav_data = await asyncio.get_event_loop().run_in_executor(
|
|
3308
|
+
None,
|
|
3309
|
+
self._decode_audio_to_wav,
|
|
3310
|
+
audio_data,
|
|
3311
|
+
silence_ms,
|
|
3304
3312
|
)
|
|
3305
|
-
|
|
3313
|
+
except Exception as e:
|
|
3314
|
+
logger.error("Failed to decode audio: %s", e)
|
|
3315
|
+
return web.json_response({"error": "Audio conversion failed"})
|
|
3306
3316
|
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3317
|
+
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as f:
|
|
3318
|
+
f.write(wav_data)
|
|
3319
|
+
wav_path = f.name
|
|
3310
3320
|
|
|
3311
|
-
|
|
3321
|
+
try:
|
|
3312
3322
|
logger.info("Transcribing %s via %s backend", wav_path, type(self.stt).__name__)
|
|
3313
3323
|
text = await self.stt.transcribe(Path(wav_path))
|
|
3314
3324
|
logger.info("Transcription result: %s", text)
|
|
3315
3325
|
return web.json_response({"text": text})
|
|
3316
3326
|
finally:
|
|
3317
|
-
Path(webm_path).unlink(missing_ok=True)
|
|
3318
3327
|
Path(wav_path).unlink(missing_ok=True)
|
|
3319
3328
|
|
|
3320
3329
|
except Exception as e:
|
|
3321
3330
|
logger.error(f"Transcription failed: {e}")
|
|
3322
3331
|
return web.json_response({"error": str(e)})
|
|
3323
3332
|
|
|
3333
|
+
@staticmethod
|
|
3334
|
+
def _decode_audio_to_wav(audio_data: bytes, silence_prepend_ms: int = 0) -> bytes:
|
|
3335
|
+
"""Decode arbitrary input audio (WebM/Opus, MP3, M4A, …) to 16 kHz mono PCM16 WAV.
|
|
3336
|
+
|
|
3337
|
+
Replaces the previous ``ffmpeg -i in.webm out.wav`` subprocess. Subprocess
|
|
3338
|
+
cold-start was 100–300 ms before any actual decoding; PyAV uses libav
|
|
3339
|
+
bindings in-process so the only cost is the decoding itself.
|
|
3340
|
+
"""
|
|
3341
|
+
import io
|
|
3342
|
+
import wave
|
|
3343
|
+
|
|
3344
|
+
import av # PyAV — declared in pyproject.toml `dependencies`
|
|
3345
|
+
|
|
3346
|
+
target_rate = 16000
|
|
3347
|
+
|
|
3348
|
+
with av.open(io.BytesIO(audio_data), mode="r") as container:
|
|
3349
|
+
if not container.streams.audio:
|
|
3350
|
+
raise RuntimeError("Input contains no audio stream")
|
|
3351
|
+
|
|
3352
|
+
resampler = av.AudioResampler(format="s16", layout="mono", rate=target_rate)
|
|
3353
|
+
pcm_chunks: list[bytes] = []
|
|
3354
|
+
|
|
3355
|
+
def _frame_bytes(f) -> bytes:
|
|
3356
|
+
# AudioFrame.planes[0] may include SIMD alignment padding; slice
|
|
3357
|
+
# to the exact PCM length (samples × channels × bytes_per_sample).
|
|
3358
|
+
bytes_per_sample = f.format.bytes # 2 for s16
|
|
3359
|
+
channels = len(f.layout.channels) # 1 for mono
|
|
3360
|
+
size = f.samples * channels * bytes_per_sample
|
|
3361
|
+
return bytes(f.planes[0])[:size]
|
|
3362
|
+
|
|
3363
|
+
for frame in container.decode(audio=0):
|
|
3364
|
+
for resampled in resampler.resample(frame):
|
|
3365
|
+
pcm_chunks.append(_frame_bytes(resampled))
|
|
3366
|
+
for resampled in resampler.resample(None):
|
|
3367
|
+
pcm_chunks.append(_frame_bytes(resampled))
|
|
3368
|
+
|
|
3369
|
+
if silence_prepend_ms > 0:
|
|
3370
|
+
silence_samples = int(target_rate * silence_prepend_ms / 1000)
|
|
3371
|
+
pcm_chunks.insert(0, b"\x00\x00" * silence_samples)
|
|
3372
|
+
|
|
3373
|
+
pcm_data = b"".join(pcm_chunks)
|
|
3374
|
+
buf = io.BytesIO()
|
|
3375
|
+
with wave.open(buf, "wb") as wav:
|
|
3376
|
+
wav.setnchannels(1)
|
|
3377
|
+
wav.setsampwidth(2) # 16-bit
|
|
3378
|
+
wav.setframerate(target_rate)
|
|
3379
|
+
wav.writeframes(pcm_data)
|
|
3380
|
+
return buf.getvalue()
|
|
3381
|
+
|
|
3324
3382
|
async def handle_upload(self, request: web.Request) -> web.Response:
|
|
3325
3383
|
"""Upload an image file for attachment to messages."""
|
|
3326
3384
|
try:
|
|
@@ -1162,6 +1162,111 @@ body {
|
|
|
1162
1162
|
text-align: center;
|
|
1163
1163
|
}
|
|
1164
1164
|
|
|
1165
|
+
/* =========================================================================
|
|
1166
|
+
Command palette (Cmd/Ctrl+K)
|
|
1167
|
+
========================================================================= */
|
|
1168
|
+
|
|
1169
|
+
.cmdk-overlay {
|
|
1170
|
+
align-items: flex-start;
|
|
1171
|
+
padding-top: 12vh;
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
.command-palette {
|
|
1175
|
+
width: 560px;
|
|
1176
|
+
max-width: 92vw;
|
|
1177
|
+
display: flex;
|
|
1178
|
+
flex-direction: column;
|
|
1179
|
+
overflow: hidden;
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
.cmdk-search {
|
|
1183
|
+
display: flex;
|
|
1184
|
+
align-items: center;
|
|
1185
|
+
gap: 10px;
|
|
1186
|
+
padding: 12px 16px;
|
|
1187
|
+
border-bottom: 1px solid var(--chrome-border);
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
.cmdk-search-icon {
|
|
1191
|
+
color: var(--text-muted);
|
|
1192
|
+
font-size: 16px;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
.cmdk-input {
|
|
1196
|
+
flex: 1;
|
|
1197
|
+
background: transparent;
|
|
1198
|
+
border: none;
|
|
1199
|
+
outline: none;
|
|
1200
|
+
color: var(--text);
|
|
1201
|
+
font-size: 16px;
|
|
1202
|
+
font-family: inherit;
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
.cmdk-input::placeholder {
|
|
1206
|
+
color: var(--text-muted);
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
.cmdk-body {
|
|
1210
|
+
padding: 8px;
|
|
1211
|
+
max-height: 50vh;
|
|
1212
|
+
overflow-y: auto;
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
/* Form drill-ins reuse the quicktask form styling. */
|
|
1216
|
+
.cmdk-body .quicktask-form {
|
|
1217
|
+
padding: 8px 8px 4px;
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
.cmdk-item {
|
|
1221
|
+
display: flex;
|
|
1222
|
+
align-items: center;
|
|
1223
|
+
gap: 12px;
|
|
1224
|
+
padding: 10px 12px;
|
|
1225
|
+
border-radius: 6px;
|
|
1226
|
+
cursor: pointer;
|
|
1227
|
+
font-size: 14px;
|
|
1228
|
+
color: var(--text);
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
.cmdk-item-selected {
|
|
1232
|
+
background: var(--accent);
|
|
1233
|
+
color: var(--background);
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
.cmdk-item-icon {
|
|
1237
|
+
width: 18px;
|
|
1238
|
+
text-align: center;
|
|
1239
|
+
opacity: 0.85;
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
.cmdk-item-label {
|
|
1243
|
+
flex: 1;
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
.cmdk-item-sub {
|
|
1247
|
+
font-size: 12px;
|
|
1248
|
+
color: var(--text-muted);
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
.cmdk-item-selected .cmdk-item-sub {
|
|
1252
|
+
color: var(--background);
|
|
1253
|
+
opacity: 0.8;
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
.cmdk-empty {
|
|
1257
|
+
padding: 20px 12px;
|
|
1258
|
+
text-align: center;
|
|
1259
|
+
color: var(--text-muted);
|
|
1260
|
+
font-size: 14px;
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
.cmdk-footer {
|
|
1264
|
+
padding: 8px 16px;
|
|
1265
|
+
border-top: 1px solid var(--chrome-border);
|
|
1266
|
+
font-size: 12px;
|
|
1267
|
+
color: var(--text-muted);
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1165
1270
|
.sidebar-quicktask {
|
|
1166
1271
|
background: transparent;
|
|
1167
1272
|
border: none;
|
|
@@ -4601,3 +4706,171 @@ body.sidebar-pinned .sidebar-tab {
|
|
|
4601
4706
|
}
|
|
4602
4707
|
|
|
4603
4708
|
|
|
4709
|
+
/* =========================================================================
|
|
4710
|
+
Pinned Message Panel — floating, draggable, resizable
|
|
4711
|
+
========================================================================= */
|
|
4712
|
+
|
|
4713
|
+
/* Pin affordance on chat messages — visible on hover, top-right corner. */
|
|
4714
|
+
.chat-message {
|
|
4715
|
+
position: relative;
|
|
4716
|
+
padding-right: 32px;
|
|
4717
|
+
}
|
|
4718
|
+
|
|
4719
|
+
.chat-message-pin-btn {
|
|
4720
|
+
position: absolute;
|
|
4721
|
+
top: 4px;
|
|
4722
|
+
right: 4px;
|
|
4723
|
+
width: 22px;
|
|
4724
|
+
height: 22px;
|
|
4725
|
+
padding: 0;
|
|
4726
|
+
border: 1px solid var(--chrome-border);
|
|
4727
|
+
border-radius: 4px;
|
|
4728
|
+
background: var(--chrome);
|
|
4729
|
+
color: var(--text-muted);
|
|
4730
|
+
font-size: 12px;
|
|
4731
|
+
line-height: 1;
|
|
4732
|
+
cursor: pointer;
|
|
4733
|
+
opacity: 0;
|
|
4734
|
+
transition: opacity 120ms ease, color 120ms ease, background 120ms ease;
|
|
4735
|
+
}
|
|
4736
|
+
|
|
4737
|
+
.chat-message:hover .chat-message-pin-btn,
|
|
4738
|
+
.chat-message:focus-within .chat-message-pin-btn,
|
|
4739
|
+
.chat-message-pin-btn:focus-visible {
|
|
4740
|
+
opacity: 0.9;
|
|
4741
|
+
}
|
|
4742
|
+
|
|
4743
|
+
.chat-message-pin-btn:hover {
|
|
4744
|
+
background: var(--hover);
|
|
4745
|
+
color: var(--accent);
|
|
4746
|
+
opacity: 1;
|
|
4747
|
+
}
|
|
4748
|
+
|
|
4749
|
+
.chat-message-pin-btn[data-pinned="true"] {
|
|
4750
|
+
opacity: 1;
|
|
4751
|
+
color: var(--accent);
|
|
4752
|
+
border-color: var(--accent);
|
|
4753
|
+
}
|
|
4754
|
+
|
|
4755
|
+
/* The floating panel itself. position: fixed keeps it above all winboxes
|
|
4756
|
+
regardless of scroll, typing, or which session window is active. */
|
|
4757
|
+
.pinned-message-panel {
|
|
4758
|
+
position: fixed;
|
|
4759
|
+
display: flex;
|
|
4760
|
+
flex-direction: column;
|
|
4761
|
+
background: var(--chrome);
|
|
4762
|
+
color: var(--text);
|
|
4763
|
+
border: 1px solid var(--chrome-border);
|
|
4764
|
+
border-radius: 8px;
|
|
4765
|
+
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.55), 0 0 0 1px var(--primary-subtle);
|
|
4766
|
+
z-index: 99998;
|
|
4767
|
+
min-width: 200px;
|
|
4768
|
+
min-height: 140px;
|
|
4769
|
+
overflow: hidden;
|
|
4770
|
+
user-select: text;
|
|
4771
|
+
}
|
|
4772
|
+
|
|
4773
|
+
.pinned-message-panel.dragging,
|
|
4774
|
+
.pinned-message-panel.resizing {
|
|
4775
|
+
user-select: none;
|
|
4776
|
+
}
|
|
4777
|
+
|
|
4778
|
+
.pinned-message-header {
|
|
4779
|
+
display: flex;
|
|
4780
|
+
align-items: center;
|
|
4781
|
+
gap: 8px;
|
|
4782
|
+
padding: 6px 10px;
|
|
4783
|
+
background: var(--background);
|
|
4784
|
+
border-bottom: 1px solid var(--chrome-border);
|
|
4785
|
+
cursor: grab;
|
|
4786
|
+
flex-shrink: 0;
|
|
4787
|
+
}
|
|
4788
|
+
|
|
4789
|
+
.pinned-message-panel.dragging .pinned-message-header {
|
|
4790
|
+
cursor: grabbing;
|
|
4791
|
+
}
|
|
4792
|
+
|
|
4793
|
+
.pinned-message-role {
|
|
4794
|
+
font-size: 11px;
|
|
4795
|
+
text-transform: uppercase;
|
|
4796
|
+
letter-spacing: 0.06em;
|
|
4797
|
+
padding: 2px 6px;
|
|
4798
|
+
border-radius: 4px;
|
|
4799
|
+
background: var(--chrome);
|
|
4800
|
+
color: var(--text-muted);
|
|
4801
|
+
border: 1px solid var(--chrome-border);
|
|
4802
|
+
flex-shrink: 0;
|
|
4803
|
+
}
|
|
4804
|
+
|
|
4805
|
+
.pinned-message-role[data-role="user"] {
|
|
4806
|
+
color: var(--accent);
|
|
4807
|
+
border-color: var(--primary-subtle);
|
|
4808
|
+
}
|
|
4809
|
+
|
|
4810
|
+
.pinned-message-title {
|
|
4811
|
+
flex: 1;
|
|
4812
|
+
font-size: 12px;
|
|
4813
|
+
color: var(--text-muted);
|
|
4814
|
+
text-transform: uppercase;
|
|
4815
|
+
letter-spacing: 0.08em;
|
|
4816
|
+
overflow: hidden;
|
|
4817
|
+
white-space: nowrap;
|
|
4818
|
+
text-overflow: ellipsis;
|
|
4819
|
+
}
|
|
4820
|
+
|
|
4821
|
+
.pinned-message-unpin {
|
|
4822
|
+
width: 22px;
|
|
4823
|
+
height: 22px;
|
|
4824
|
+
padding: 0;
|
|
4825
|
+
border: 1px solid var(--chrome-border);
|
|
4826
|
+
border-radius: 4px;
|
|
4827
|
+
background: transparent;
|
|
4828
|
+
color: var(--text-muted);
|
|
4829
|
+
font-size: 14px;
|
|
4830
|
+
line-height: 1;
|
|
4831
|
+
cursor: pointer;
|
|
4832
|
+
transition: color 120ms ease, background 120ms ease, border-color 120ms ease;
|
|
4833
|
+
flex-shrink: 0;
|
|
4834
|
+
}
|
|
4835
|
+
|
|
4836
|
+
.pinned-message-unpin:hover {
|
|
4837
|
+
color: var(--error);
|
|
4838
|
+
border-color: var(--error);
|
|
4839
|
+
background: var(--hover);
|
|
4840
|
+
}
|
|
4841
|
+
|
|
4842
|
+
.pinned-message-body {
|
|
4843
|
+
flex: 1;
|
|
4844
|
+
overflow: auto;
|
|
4845
|
+
padding: 10px 12px;
|
|
4846
|
+
font-size: 14px;
|
|
4847
|
+
line-height: 1.45;
|
|
4848
|
+
color: var(--text);
|
|
4849
|
+
white-space: pre-wrap;
|
|
4850
|
+
word-break: break-word;
|
|
4851
|
+
}
|
|
4852
|
+
|
|
4853
|
+
/* Corner resize handle — visible on hover. */
|
|
4854
|
+
.pinned-message-resize-handle {
|
|
4855
|
+
position: absolute;
|
|
4856
|
+
right: 0;
|
|
4857
|
+
bottom: 0;
|
|
4858
|
+
width: 16px;
|
|
4859
|
+
height: 16px;
|
|
4860
|
+
cursor: nwse-resize;
|
|
4861
|
+
opacity: 0;
|
|
4862
|
+
transition: opacity 120ms ease;
|
|
4863
|
+
/* Diagonal grip lines drawn with two thin gradients. */
|
|
4864
|
+
background:
|
|
4865
|
+
linear-gradient(135deg, transparent 0 45%, var(--text-muted) 45% 55%, transparent 55% 100%) no-repeat,
|
|
4866
|
+
linear-gradient(135deg, transparent 0 70%, var(--text-muted) 70% 80%, transparent 80% 100%) no-repeat;
|
|
4867
|
+
background-size: 10px 10px, 6px 6px;
|
|
4868
|
+
background-position: bottom 3px right 3px, bottom 3px right 3px;
|
|
4869
|
+
}
|
|
4870
|
+
|
|
4871
|
+
.pinned-message-panel:hover .pinned-message-resize-handle,
|
|
4872
|
+
.pinned-message-panel.resizing .pinned-message-resize-handle {
|
|
4873
|
+
opacity: 0.85;
|
|
4874
|
+
}
|
|
4875
|
+
|
|
4876
|
+
|