agentwire-dev 1.31.0__tar.gz → 1.33.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.31.0 → agentwire_dev-1.33.0}/PKG-INFO +1 -1
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/__init__.py +1 -1
- agentwire_dev-1.33.0/agentwire/static/announcements.json +31 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/css/desktop.css +217 -0
- agentwire_dev-1.33.0/agentwire/static/js/announcement-modal.js +178 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/desktop.js +16 -13
- agentwire_dev-1.33.0/agentwire/static/js/notification-prefs.js +56 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/sidebar/config-section.js +56 -2
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/templates/desktop.html +1 -0
- agentwire_dev-1.33.0/tests/unit/test_announcements.py +60 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/.github/FUNDING.yml +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/.github/ISSUE_TEMPLATE/question.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/.gitignore +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/CHANGELOG.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/CLA.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/CODE_OF_CONDUCT.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/CONTRIBUTING.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/Dockerfile.local +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/Dockerfile.runpod +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/LICENSE +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/README.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/RELEASING.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/SECURITY.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/SPONSORS.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/__main__.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/agents/__init__.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/agents/base.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/agents/tmux.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/cached_status.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/channels/__init__.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/channels/base.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/channels/email.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/channels/quo.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/cli_safety.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/completion.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/config.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/council/__init__.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/council/cli.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/council/inbox.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/council/state.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/fetch.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/handoff/__init__.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/handoff/git_state.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/handoff/instructions.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/handoff/parser.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/handoff/renderer.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/handoff/schema.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/history.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/hooks/__init__.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/hooks/agentwire-permission.sh +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/hooks/damage-control/__init__.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/hooks/damage-control/audit_logger.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/hooks/damage-control/bash-tool-damage-control.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/hooks/damage-control/edit-tool-damage-control.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/hooks/damage-control/rules/agentwire.yaml +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/hooks/damage-control/rules/aws.yaml +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/hooks/damage-control/rules/cloud-hosting.yaml +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/hooks/damage-control/rules/containers.yaml +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/hooks/damage-control/rules/core.yaml +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/hooks/damage-control/rules/databases.yaml +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/hooks/damage-control/rules/firebase.yaml +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/hooks/damage-control/rules/gcp.yaml +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/hooks/damage-control/rules/git.yaml +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/hooks/damage-control/rules/gws.yaml +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/hooks/damage-control/rules/infrastructure.yaml +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/hooks/damage-control/rules/remote.yaml +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/hooks/damage-control/write-tool-damage-control.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/hooks/idle-handler.sh +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/hooks/queue-processor.sh +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/listen.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/locking.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/mcp_server.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/missions/__init__.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/missions/cli.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/missions/config.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/missions/dispatcher.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/missions/eligibility.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/missions/feedback_router.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/missions/gc.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/missions/github.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/missions/naming.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/missions/state.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/network.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/onboarding.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/overnight.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/pane_manager.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/project_config.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/projects.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/prompts/__init__.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/prompts/init.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/roles/__init__.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/roles/agentwire.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/roles/chatbot.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/roles/council-brain.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/roles/council-conscience.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/roles/council-critic.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/roles/council-devils-advocate.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/roles/council-gut.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/roles/council-historian.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/roles/council-member.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/roles/council-orchestrator.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/roles/init.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/roles/notifications.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/roles/orchestrator.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/roles/soul.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/roles/task-runner.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/roles/voice.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/roles/worker.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/safety/__init__.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/safety/_core.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/scheduler.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/scratchpad.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/search.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/security.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/server.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/services.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/session_ready.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/agentwire-Echo--black.png +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/agentwire-Echo--transparent.png +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/agentwire-Echo.png +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/agentwire-email-banner.png +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/agentwire-splash-logo-layers--agentwire-text.png +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/agentwire-splash-logo-layers--echo-claw-fg.png +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/agentwire-splash-logo-layers--echo.png +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/agentwire-splash-logo-layers--full--transparent-top.png +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/agentwire-splash-logo-layers--full-black.png +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/agentwire-splash-logo-layers--telephone-fg.png +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/agentwire-splash-logo-layers--telephone.png +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/agentwire-splash-logo-layers--transparent-top.png +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/agentwire-splash-logo-layers--transparent.png +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/agentwire-splash-logo-layers--tree.png +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/agentwire-splash-logo-layers.png +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/favicon.png +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/machines/android.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/machines/automaton.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/machines/bot.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/machines/cyborg.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/machines/droid.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/machines/drone.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/machines/guardian.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/machines/mech.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/machines/probe.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/machines/robot.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/machines/sentinel.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/machines/unit.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/projects/blob.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/projects/cloud.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/projects/crystal.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/projects/cyclops.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/projects/flame.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/projects/fuzzy.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/projects/horned.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/projects/moon.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/projects/slime.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/projects/star.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/projects/tentacle.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/projects/winged.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/sessions/bear.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/sessions/cat.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/sessions/crown.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/sessions/custom/agentwire-portal.png +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/sessions/custom/agentwire-tts.png +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/sessions/custom/agentwire.png +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/sessions/deer.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/sessions/drone.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/sessions/eagle.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/sessions/fox.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/sessions/hawk.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/sessions/horse.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/sessions/lion.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/sessions/rabbit.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/sessions/robot.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/sessions/tiger.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/icons/sessions/wolf.jpeg +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/.gitkeep +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/api.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/artifact-window.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/collage.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/command-palette.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/components/icon-picker.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/components/list-card.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/components/type-tag.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/dead-key-suppressor.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/desktop-manager.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/icon-manager.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/notifications-panel.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/safety-shared.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/safety-window.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/scratchpad.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/session-id.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/session-window.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/sidebar/artifacts-section.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/sidebar/machines-section.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/sidebar/missions-section.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/sidebar/projects-section.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/sidebar/safety-section.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/sidebar/scheduler-section.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/sidebar/services-section.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/sidebar/sessions-section.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/sidebar.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/terminal-font-prefs.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/tile-manager.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/token-modal.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/utils/ansi.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/utils/auto-refresh.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/voice/browser-stt.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/voice/browser-tts.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/voice/jargon.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/voice/prompt.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/static/js/winbox.bundle.min.js +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/stt/__init__.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/stt/base.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/stt/server_backend.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/stt/stt_server.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/tasks.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/templates/__init__.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/templates/base.html +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/templates/email_notification.html +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/templates/handoff/show-the-story.html.j2 +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/templates/handoff/theme.css.j2 +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/templates/tmux.conf +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/templating.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/tooldefs/aws.yaml +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/tooldefs/docker.yaml +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/tooldefs/gcp.yaml +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/tooldefs/gh.yaml +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/tooldefs/git.yaml +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/tooldefs/gws.yaml +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/tooldefs/kubectl.yaml +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/tooldefs/npm.yaml +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/tooldefs/terraform.yaml +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/tooldefs/uv.yaml +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/tts/__init__.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/tts/base.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/tts/engines/__init__.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/tts/engines/chatterbox.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/tts/engines/kokoro.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/tts/engines/qwen_base.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/tts/engines/qwen_custom.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/tts/engines/qwen_design.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/tts/engines/zonos.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/tts/registry.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/tts_server.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/tunnels.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/utils/__init__.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/utils/chunker.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/utils/file_io.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/utils/paths.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/utils/speech.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/utils/subprocess.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/validation.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/voiceclone.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/voices/darren.wav +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/voices/default.wav +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/voices/jessica.wav +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/voices/lisa.wav +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/voices/may.wav +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/agentwire/worktree.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/docs/decisions/obsidian-second-brain.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/docs/logo.png +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/docs/wiki/INDEX.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/docs/wiki/architecture.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/docs/wiki/communication/channels.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/docs/wiki/communication/hammerspoon.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/docs/wiki/communication/handoff.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/docs/wiki/concepts.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/docs/wiki/council.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/docs/wiki/deployment/remote-access.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/docs/wiki/deployment/remote-machines.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/docs/wiki/glossary.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/docs/wiki/integrations/gws-google-workspace-cli.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/docs/wiki/internals/damage-control.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/docs/wiki/internals/portal.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/docs/wiki/internals/shell-escaping.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/docs/wiki/internals/troubleshooting.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/docs/wiki/internals/window-collage.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/docs/wiki/missions.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/docs/wiki/quickstart.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/docs/wiki/scheduling/scheduled-workloads.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/docs/wiki/services.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/docs/wiki/sessions/claude-code-auto-mode.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/docs/wiki/sessions/pi.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/docs/wiki/voice/shim-contract.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/docs/wiki/voice/stt-self-hosted.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/docs/wiki/voice/tts-self-hosted.md +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/pyproject.toml +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/requirements-tts.txt +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/templates/launchd/dev.agentwire.mission-dispatcher.plist +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/templates/launchd/dev.agentwire.mission-feedback-router.plist +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/templates/launchd/dev.agentwire.mission-janitor.plist +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/conftest.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/e2e/test_portal_ui.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/fixtures/sample_agentwire.yml +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/fixtures/sample_config.yaml +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/fixtures/sample_scheduler.yaml +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/integration/test_missions_concurrency.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/integration/test_missions_lifecycle.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/integration/test_scheduler_board.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/integration/test_server_websockets.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_build_agent_command.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_channels.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_cli_commands.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_cli_output.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_cli_safety.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_config.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_council_cli.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_council_inbox.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_council_state.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_damage_control_hooks.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_damage_control_sync.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_file_io.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_handoff_git_state.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_handoff_instructions.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_handoff_parser.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_handoff_renderer.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_history.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_hooks_install.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_idle_handler.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_locking.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_mcp_server.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_mcp_tools_args.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_missions_cli.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_missions_config.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_missions_dispatcher.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_missions_eligibility.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_missions_feedback_router.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_missions_gc.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_missions_github.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_missions_naming.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_missions_state.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_overnight_resume_flags.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_portal_api.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_project_config.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_roles.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_safety_disabled_rules.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_safety_escape_hatch.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_safety_kill_switch.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_safety_mission_worker.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_scheduler.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_scheduler_parsing.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_scratchpad.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_search.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_security.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_server_async.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_server_pure.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_services.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_session_ready.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_speech_tags.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_stt_backend.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_tasks.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_templating.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_tmux_template.py +0 -0
- {agentwire_dev-1.31.0 → agentwire_dev-1.33.0}/tests/unit/test_worktree.py +0 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"announcements": [
|
|
3
|
+
{
|
|
4
|
+
"id": "evergreen-feedback-2026-06",
|
|
5
|
+
"placement": "banner",
|
|
6
|
+
"emoji": "👋",
|
|
7
|
+
"title": "You're an early one",
|
|
8
|
+
"body": "AgentWire is young and shaped by the handful of people running it. Bug, idea, or just hello — open an issue, it's read.",
|
|
9
|
+
"cta": {
|
|
10
|
+
"label": "Open an issue",
|
|
11
|
+
"url": "https://github.com/dotdevdotdev/agentwire-dev/issues/new"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"id": "2026-06-idea-first-capture",
|
|
16
|
+
"placement": "modal",
|
|
17
|
+
"emoji": "💡",
|
|
18
|
+
"title": "New idea? Cmd+K and go.",
|
|
19
|
+
"date": "2026-06-08",
|
|
20
|
+
"body": "AgentWire can now spin up a whole project from a single sentence. Press Cmd/Ctrl+K (or the New idea button), type — or speak — what you want to build, and it scaffolds the folder, starts an agent, and hands it your idea as the first message. You watch it get to work the moment the window opens.",
|
|
21
|
+
"highlights": [
|
|
22
|
+
"Idea-first capture — type an idea, get a running agent",
|
|
23
|
+
"Instant voice mode — browser speech in and out, zero setup"
|
|
24
|
+
],
|
|
25
|
+
"cta": {
|
|
26
|
+
"label": "See what's in v1.31",
|
|
27
|
+
"url": "https://github.com/dotdevdotdev/agentwire-dev/releases/tag/v1.31.0"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
}
|
|
@@ -770,6 +770,25 @@ body {
|
|
|
770
770
|
border-color: var(--accent);
|
|
771
771
|
}
|
|
772
772
|
|
|
773
|
+
.sidebar-notif-mute {
|
|
774
|
+
display: flex;
|
|
775
|
+
align-items: center;
|
|
776
|
+
gap: 6px;
|
|
777
|
+
color: var(--text-muted);
|
|
778
|
+
font-size: 13px;
|
|
779
|
+
cursor: pointer;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
.sidebar-notif-mute input[type="checkbox"] {
|
|
783
|
+
accent-color: var(--accent);
|
|
784
|
+
cursor: pointer;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
.sidebar-display-hint {
|
|
788
|
+
color: var(--text-muted);
|
|
789
|
+
font-size: 12px;
|
|
790
|
+
}
|
|
791
|
+
|
|
773
792
|
/* Voice Indicator States */
|
|
774
793
|
#sidebarVoiceIndicator {
|
|
775
794
|
display: flex;
|
|
@@ -1000,6 +1019,151 @@ body {
|
|
|
1000
1019
|
font-weight: 500;
|
|
1001
1020
|
}
|
|
1002
1021
|
|
|
1022
|
+
/* =========================================================================
|
|
1023
|
+
Announcement modal (userbase welcome card)
|
|
1024
|
+
========================================================================= */
|
|
1025
|
+
|
|
1026
|
+
.announcement-overlay {
|
|
1027
|
+
z-index: 2100; /* above standard modals, below the command palette */
|
|
1028
|
+
animation: announcement-fade 180ms ease-out;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
.announcement-overlay .announcement-modal {
|
|
1032
|
+
width: 460px;
|
|
1033
|
+
max-width: 92vw;
|
|
1034
|
+
padding: 28px 28px 22px;
|
|
1035
|
+
text-align: center;
|
|
1036
|
+
background: linear-gradient(180deg, color-mix(in srgb, var(--accent) 7%, var(--chrome)) 0%, var(--chrome) 60%);
|
|
1037
|
+
border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--chrome-border));
|
|
1038
|
+
border-radius: 12px;
|
|
1039
|
+
animation: announcement-rise 220ms cubic-bezier(0.2, 0.8, 0.2, 1);
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
.announcement-dismiss {
|
|
1043
|
+
position: absolute;
|
|
1044
|
+
top: 12px;
|
|
1045
|
+
right: 12px;
|
|
1046
|
+
background: transparent;
|
|
1047
|
+
border: none;
|
|
1048
|
+
color: var(--text-muted);
|
|
1049
|
+
font-size: 15px;
|
|
1050
|
+
cursor: pointer;
|
|
1051
|
+
line-height: 1;
|
|
1052
|
+
padding: 4px 6px;
|
|
1053
|
+
border-radius: 4px;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
.announcement-dismiss:hover {
|
|
1057
|
+
color: var(--text);
|
|
1058
|
+
background: var(--background);
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
.announcement-emoji {
|
|
1062
|
+
font-size: 40px;
|
|
1063
|
+
line-height: 1;
|
|
1064
|
+
margin-bottom: 10px;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
.announcement-title {
|
|
1068
|
+
margin: 0 0 4px;
|
|
1069
|
+
font-size: 21px;
|
|
1070
|
+
font-weight: 700;
|
|
1071
|
+
color: var(--text);
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
.announcement-date {
|
|
1075
|
+
font-size: 12px;
|
|
1076
|
+
color: var(--text-muted);
|
|
1077
|
+
margin-bottom: 14px;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
.announcement-body {
|
|
1081
|
+
font-size: 15px;
|
|
1082
|
+
line-height: 1.55;
|
|
1083
|
+
color: var(--text);
|
|
1084
|
+
opacity: 0.92;
|
|
1085
|
+
margin-bottom: 16px;
|
|
1086
|
+
text-align: left;
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
.announcement-highlights {
|
|
1090
|
+
list-style: none;
|
|
1091
|
+
margin: 0 0 20px;
|
|
1092
|
+
padding: 0;
|
|
1093
|
+
text-align: left;
|
|
1094
|
+
display: flex;
|
|
1095
|
+
flex-direction: column;
|
|
1096
|
+
gap: 8px;
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
.announcement-highlights li {
|
|
1100
|
+
position: relative;
|
|
1101
|
+
padding: 8px 12px 8px 34px;
|
|
1102
|
+
background: var(--background);
|
|
1103
|
+
border: 1px solid var(--chrome-border);
|
|
1104
|
+
border-radius: 6px;
|
|
1105
|
+
font-size: 14px;
|
|
1106
|
+
color: var(--text);
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
.announcement-highlights li::before {
|
|
1110
|
+
content: "✦";
|
|
1111
|
+
position: absolute;
|
|
1112
|
+
left: 12px;
|
|
1113
|
+
top: 8px;
|
|
1114
|
+
color: var(--accent);
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
.announcement-footer {
|
|
1118
|
+
display: flex;
|
|
1119
|
+
align-items: center;
|
|
1120
|
+
justify-content: center;
|
|
1121
|
+
gap: 10px;
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
.announcement-cta {
|
|
1125
|
+
flex: 1;
|
|
1126
|
+
padding: 10px 16px;
|
|
1127
|
+
background: var(--accent);
|
|
1128
|
+
color: var(--background);
|
|
1129
|
+
border-radius: 6px;
|
|
1130
|
+
font-weight: 600;
|
|
1131
|
+
font-size: 14px;
|
|
1132
|
+
text-decoration: none;
|
|
1133
|
+
text-align: center;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
.announcement-cta:hover {
|
|
1137
|
+
filter: brightness(1.08);
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
.announcement-ok {
|
|
1141
|
+
flex: 1;
|
|
1142
|
+
padding: 10px 16px;
|
|
1143
|
+
background: transparent;
|
|
1144
|
+
border: 1px solid var(--chrome-border);
|
|
1145
|
+
border-radius: 6px;
|
|
1146
|
+
color: var(--text);
|
|
1147
|
+
font-weight: 600;
|
|
1148
|
+
font-size: 14px;
|
|
1149
|
+
cursor: pointer;
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
.announcement-ok:hover {
|
|
1153
|
+
border-color: var(--accent);
|
|
1154
|
+
color: var(--accent);
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
@keyframes announcement-fade {
|
|
1158
|
+
from { opacity: 0; }
|
|
1159
|
+
to { opacity: 1; }
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
@keyframes announcement-rise {
|
|
1163
|
+
from { opacity: 0; transform: translateY(12px) scale(0.98); }
|
|
1164
|
+
to { opacity: 1; transform: translateY(0) scale(1); }
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1003
1167
|
/* =========================================================================
|
|
1004
1168
|
Quicktask modal
|
|
1005
1169
|
========================================================================= */
|
|
@@ -4820,6 +4984,59 @@ body.sidebar-pinned .sidebar-tab {
|
|
|
4820
4984
|
opacity: 1;
|
|
4821
4985
|
}
|
|
4822
4986
|
|
|
4987
|
+
/* Sidebar announcement banner (evergreen userbase note — see announcement-modal.js) */
|
|
4988
|
+
.announcement-banner {
|
|
4989
|
+
position: relative;
|
|
4990
|
+
flex-basis: 100%;
|
|
4991
|
+
padding: calc(var(--spacing-base) * 1.25);
|
|
4992
|
+
margin-bottom: var(--spacing-base);
|
|
4993
|
+
border: 1px solid color-mix(in srgb, var(--accent) 55%, var(--chrome-border));
|
|
4994
|
+
border-left: 3px solid var(--accent);
|
|
4995
|
+
border-radius: var(--radius, 6px);
|
|
4996
|
+
background: color-mix(in srgb, var(--accent) 6%, var(--chrome));
|
|
4997
|
+
font-size: 11px;
|
|
4998
|
+
line-height: 1.45;
|
|
4999
|
+
}
|
|
5000
|
+
|
|
5001
|
+
.announcement-banner-title {
|
|
5002
|
+
font-weight: 600;
|
|
5003
|
+
color: var(--accent);
|
|
5004
|
+
margin-bottom: 4px;
|
|
5005
|
+
padding-right: 14px; /* clear the ✕ */
|
|
5006
|
+
}
|
|
5007
|
+
|
|
5008
|
+
.announcement-banner-body {
|
|
5009
|
+
color: var(--text-secondary, inherit);
|
|
5010
|
+
}
|
|
5011
|
+
|
|
5012
|
+
.announcement-banner-cta {
|
|
5013
|
+
display: inline-block;
|
|
5014
|
+
margin-top: 6px;
|
|
5015
|
+
color: var(--accent);
|
|
5016
|
+
font-weight: 600;
|
|
5017
|
+
text-decoration: none;
|
|
5018
|
+
}
|
|
5019
|
+
|
|
5020
|
+
.announcement-banner-cta:hover {
|
|
5021
|
+
text-decoration: underline;
|
|
5022
|
+
}
|
|
5023
|
+
|
|
5024
|
+
.announcement-banner-dismiss {
|
|
5025
|
+
position: absolute;
|
|
5026
|
+
top: 4px;
|
|
5027
|
+
right: 6px;
|
|
5028
|
+
border: none;
|
|
5029
|
+
background: none;
|
|
5030
|
+
color: inherit;
|
|
5031
|
+
cursor: pointer;
|
|
5032
|
+
font-size: 11px;
|
|
5033
|
+
opacity: 0.6;
|
|
5034
|
+
}
|
|
5035
|
+
|
|
5036
|
+
.announcement-banner-dismiss:hover {
|
|
5037
|
+
opacity: 1;
|
|
5038
|
+
}
|
|
5039
|
+
|
|
4823
5040
|
/* Edit-before-send transcript bars (default STT tier) */
|
|
4824
5041
|
.sidebar-transcript-bar,
|
|
4825
5042
|
.wb-transcript-bar {
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Announcements — userbase messaging in two channels, one source file.
|
|
3
|
+
*
|
|
4
|
+
* Content is a single JSON file (agentwire/static/announcements.json) edited
|
|
5
|
+
* on `main`. It plays two roles:
|
|
6
|
+
* - Remote source: fetched from raw.githubusercontent on load, so an edit
|
|
7
|
+
* on `main` reaches every user on their next portal open — no upgrade.
|
|
8
|
+
* - Bundled fallback: the same file ships in the package and is served at
|
|
9
|
+
* /static/announcements.json, used when the remote GET fails (offline).
|
|
10
|
+
*
|
|
11
|
+
* Each entry's `placement` picks the channel:
|
|
12
|
+
* - "modal" (default) — big centered splash for the moments that matter.
|
|
13
|
+
* - "banner" — a quiet, persistent strip in the sidebar footer for
|
|
14
|
+
* evergreen notes (a link, a tip, an ask).
|
|
15
|
+
* Both show the newest entry whose id isn't in the shared dismissed set
|
|
16
|
+
* (localStorage `aw-announcements-seen`); dismiss is per-id.
|
|
17
|
+
*
|
|
18
|
+
* The fetch is a single unauthenticated GET with nothing about the user in
|
|
19
|
+
* it — no telemetry. Content fields are rendered as ESCAPED TEXT (the
|
|
20
|
+
* frontend owns all markup), so even though the body comes from a URL there
|
|
21
|
+
* is no injection surface; the CTA url is validated to http(s).
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const REMOTE_URL = 'https://raw.githubusercontent.com/dotdevdotdev/agentwire-dev/main/agentwire/static/announcements.json';
|
|
25
|
+
const LOCAL_URL = '/static/announcements.json';
|
|
26
|
+
const SEEN_KEY = 'aw-announcements-seen';
|
|
27
|
+
const FETCH_TIMEOUT_MS = 4000;
|
|
28
|
+
|
|
29
|
+
let modalEl = null;
|
|
30
|
+
let escHandler = null;
|
|
31
|
+
|
|
32
|
+
function escapeHtml(s) {
|
|
33
|
+
return String(s).replace(/[&<>"']/g, (c) => (
|
|
34
|
+
{ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]
|
|
35
|
+
));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function seenIds() {
|
|
39
|
+
try {
|
|
40
|
+
const raw = localStorage.getItem(SEEN_KEY);
|
|
41
|
+
const arr = raw ? JSON.parse(raw) : [];
|
|
42
|
+
return Array.isArray(arr) ? arr : [];
|
|
43
|
+
} catch {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function markSeen(id) {
|
|
49
|
+
const ids = seenIds();
|
|
50
|
+
if (!ids.includes(id)) {
|
|
51
|
+
ids.push(id);
|
|
52
|
+
try { localStorage.setItem(SEEN_KEY, JSON.stringify(ids)); } catch { /* ignore */ }
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function fetchJson(url) {
|
|
57
|
+
const ctrl = new AbortController();
|
|
58
|
+
const timer = setTimeout(() => ctrl.abort(), FETCH_TIMEOUT_MS);
|
|
59
|
+
try {
|
|
60
|
+
// No credentials, no caching, nothing about the user goes out.
|
|
61
|
+
const resp = await fetch(url, { signal: ctrl.signal, cache: 'no-store', credentials: 'omit' });
|
|
62
|
+
if (!resp.ok) return null;
|
|
63
|
+
return await resp.json();
|
|
64
|
+
} catch {
|
|
65
|
+
return null;
|
|
66
|
+
} finally {
|
|
67
|
+
clearTimeout(timer);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** A valid, safe http(s) URL, or null. */
|
|
72
|
+
function safeUrl(url) {
|
|
73
|
+
try {
|
|
74
|
+
const u = new URL(String(url));
|
|
75
|
+
return (u.protocol === 'https:' || u.protocol === 'http:') ? u.href : null;
|
|
76
|
+
} catch {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Newest (file order) unseen announcement matching `placement` (default "modal"). */
|
|
82
|
+
function pickUnseen(list, placement) {
|
|
83
|
+
const seen = seenIds();
|
|
84
|
+
return list.find((a) => (
|
|
85
|
+
a && typeof a.id === 'string' && a.id
|
|
86
|
+
&& (a.placement || 'modal') === placement
|
|
87
|
+
&& !seen.includes(a.id)
|
|
88
|
+
)) || null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function renderModal(a) {
|
|
92
|
+
const emoji = a.emoji ? `<div class="announcement-emoji">${escapeHtml(a.emoji)}</div>` : '';
|
|
93
|
+
const date = a.date ? `<div class="announcement-date">${escapeHtml(a.date)}</div>` : '';
|
|
94
|
+
const highlights = Array.isArray(a.highlights) && a.highlights.length
|
|
95
|
+
? `<ul class="announcement-highlights">${a.highlights
|
|
96
|
+
.map((h) => `<li>${escapeHtml(h)}</li>`).join('')}</ul>`
|
|
97
|
+
: '';
|
|
98
|
+
const ctaUrl = a.cta && safeUrl(a.cta.url);
|
|
99
|
+
const cta = ctaUrl
|
|
100
|
+
? `<a class="announcement-cta" href="${escapeHtml(ctaUrl)}" target="_blank" rel="noopener noreferrer">${escapeHtml(a.cta.label || 'Learn more')}</a>`
|
|
101
|
+
: '';
|
|
102
|
+
return `<div class="modal-overlay announcement-overlay" id="announcementOverlay">
|
|
103
|
+
<div class="modal announcement-modal" role="dialog" aria-label="Announcement" aria-modal="true">
|
|
104
|
+
<button class="announcement-dismiss" data-action="dismiss" title="Dismiss" aria-label="Dismiss">✕</button>
|
|
105
|
+
${emoji}
|
|
106
|
+
<h2 class="announcement-title">${escapeHtml(a.title || 'What’s new')}</h2>
|
|
107
|
+
${date}
|
|
108
|
+
<div class="announcement-body">${escapeHtml(a.body || '')}</div>
|
|
109
|
+
${highlights}
|
|
110
|
+
<div class="announcement-footer">
|
|
111
|
+
${cta}
|
|
112
|
+
<button class="announcement-ok" data-action="dismiss">Got it</button>
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
</div>`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function closeModal(id) {
|
|
119
|
+
if (id) markSeen(id);
|
|
120
|
+
if (escHandler) { document.removeEventListener('keydown', escHandler); escHandler = null; }
|
|
121
|
+
modalEl?.remove();
|
|
122
|
+
modalEl = null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function showModal(a) {
|
|
126
|
+
if (modalEl) return;
|
|
127
|
+
const wrapper = document.createElement('div');
|
|
128
|
+
wrapper.innerHTML = renderModal(a);
|
|
129
|
+
modalEl = wrapper.firstElementChild;
|
|
130
|
+
document.body.appendChild(modalEl);
|
|
131
|
+
|
|
132
|
+
modalEl.addEventListener('click', (e) => {
|
|
133
|
+
if (e.target === modalEl || e.target.closest('[data-action="dismiss"]')) {
|
|
134
|
+
closeModal(a.id);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
escHandler = (e) => { if (e.key === 'Escape') closeModal(a.id); };
|
|
138
|
+
document.addEventListener('keydown', escHandler);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function renderBanner(a) {
|
|
142
|
+
const slot = document.getElementById('sidebarAnnouncementBanner');
|
|
143
|
+
if (!slot) return;
|
|
144
|
+
const ctaUrl = a.cta && safeUrl(a.cta.url);
|
|
145
|
+
const title = a.emoji ? `${escapeHtml(a.emoji)} ${escapeHtml(a.title || '')}` : escapeHtml(a.title || '');
|
|
146
|
+
const cta = ctaUrl
|
|
147
|
+
? `<a class="announcement-banner-cta" href="${escapeHtml(ctaUrl)}" target="_blank" rel="noopener noreferrer">${escapeHtml(a.cta.label || 'Learn more')} →</a>`
|
|
148
|
+
: '';
|
|
149
|
+
slot.innerHTML = `<div class="announcement-banner">
|
|
150
|
+
<button class="announcement-banner-dismiss" data-action="dismiss" title="Dismiss" aria-label="Dismiss announcement">✕</button>
|
|
151
|
+
${title ? `<div class="announcement-banner-title">${title}</div>` : ''}
|
|
152
|
+
${a.body ? `<div class="announcement-banner-body">${escapeHtml(a.body)}</div>` : ''}
|
|
153
|
+
${cta}
|
|
154
|
+
</div>`;
|
|
155
|
+
slot.querySelector('[data-action="dismiss"]')?.addEventListener('click', () => {
|
|
156
|
+
markSeen(a.id);
|
|
157
|
+
slot.innerHTML = '';
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Fetch announcements (remote first, bundled fallback) and surface the newest
|
|
163
|
+
* unseen entry per channel: a centered modal and a sidebar-footer banner.
|
|
164
|
+
* Fails silently — an announcement is never worth a broken portal.
|
|
165
|
+
*/
|
|
166
|
+
export async function initAnnouncements() {
|
|
167
|
+
try {
|
|
168
|
+
const data = (await fetchJson(REMOTE_URL)) || (await fetchJson(LOCAL_URL));
|
|
169
|
+
const list = Array.isArray(data?.announcements) ? data.announcements : [];
|
|
170
|
+
if (!list.length) return;
|
|
171
|
+
const modal = pickUnseen(list, 'modal');
|
|
172
|
+
if (modal) showModal(modal);
|
|
173
|
+
const banner = pickUnseen(list, 'banner');
|
|
174
|
+
if (banner) renderBanner(banner);
|
|
175
|
+
} catch {
|
|
176
|
+
/* never let an announcement break the desktop */
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -15,6 +15,7 @@ import { SessionWindow } from './session-window.js';
|
|
|
15
15
|
import { ArtifactWindow } from './artifact-window.js';
|
|
16
16
|
import { sidebar } from './sidebar.js';
|
|
17
17
|
import { buildSessionId, normalizeMachine, isLocalMachine } from './session-id.js';
|
|
18
|
+
import { notificationsActive } from './notification-prefs.js';
|
|
18
19
|
import { configSection } from './sidebar/config-section.js';
|
|
19
20
|
import { safetySection } from './sidebar/safety-section.js';
|
|
20
21
|
import { artifactsSection } from './sidebar/artifacts-section.js';
|
|
@@ -32,6 +33,7 @@ import { armDeadKeySuppressor, disarmDeadKeySuppressor } from './dead-key-suppre
|
|
|
32
33
|
import { openCommandPalette, isCommandPaletteOpen } from './command-palette.js';
|
|
33
34
|
import * as browserStt from './voice/browser-stt.js';
|
|
34
35
|
import { voicePromptWrap } from './voice/prompt.js';
|
|
36
|
+
import { initAnnouncements } from './announcement-modal.js';
|
|
35
37
|
|
|
36
38
|
// State - track open windows
|
|
37
39
|
const sessionWindows = new Map(); // sessionId -> SessionWindow instance
|
|
@@ -251,6 +253,10 @@ async function init() {
|
|
|
251
253
|
|
|
252
254
|
// Fetch initial data in the background (will emit events to listeners above)
|
|
253
255
|
desktop.fetchSessions();
|
|
256
|
+
|
|
257
|
+
// Show a userbase announcement if there's a new one (non-blocking,
|
|
258
|
+
// fails silently — never holds up or breaks the desktop).
|
|
259
|
+
initAnnouncements();
|
|
254
260
|
}
|
|
255
261
|
|
|
256
262
|
/**
|
|
@@ -310,19 +316,16 @@ function handleSessionRenamed({ old_name, new_name }) {
|
|
|
310
316
|
* Shows desktop notification for background session activity.
|
|
311
317
|
*/
|
|
312
318
|
function handleWindowActivity({ session }) {
|
|
313
|
-
// Only notify if session window
|
|
314
|
-
if (desktop.getActiveWindow()
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
Notification.requestPermission();
|
|
324
|
-
}
|
|
325
|
-
}
|
|
319
|
+
// Only notify if the session window isn't the one you're looking at.
|
|
320
|
+
if (desktop.getActiveWindow() === session) return;
|
|
321
|
+
// Respects browser permission + the Config sidebar mute toggle.
|
|
322
|
+
// Enabling is done explicitly from Config → Desktop notifications, not here.
|
|
323
|
+
if (!notificationsActive()) return;
|
|
324
|
+
new Notification(`Activity in ${session}`, {
|
|
325
|
+
body: 'Session has new output',
|
|
326
|
+
icon: '/static/img/icon-192.png',
|
|
327
|
+
tag: `activity-${session}`, // Prevent duplicate notifications
|
|
328
|
+
});
|
|
326
329
|
}
|
|
327
330
|
|
|
328
331
|
// Tab / Shift+Tab to cycle open windows
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Desktop notification preferences — frontend-only, per-browser.
|
|
3
|
+
*
|
|
4
|
+
* Two independent layers:
|
|
5
|
+
* - Browser permission ('default' | 'granted' | 'denied') — owned by the
|
|
6
|
+
* browser, requested via enableNotifications(). The portal can ask but
|
|
7
|
+
* cannot grant it; that's a per-origin browser decision.
|
|
8
|
+
* - A local "muted" toggle — lets the user silence bubbles without revoking
|
|
9
|
+
* the OS permission. Persisted in localStorage.
|
|
10
|
+
*
|
|
11
|
+
* Changing either dispatches `notification-pref-change` on window so the
|
|
12
|
+
* Config sidebar (and anything else) can repaint.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const MUTE_KEY = 'notificationsMuted';
|
|
16
|
+
export const NOTIFICATION_PREF_EVENT = 'notification-pref-change';
|
|
17
|
+
|
|
18
|
+
export function notificationsSupported() {
|
|
19
|
+
return typeof window !== 'undefined' && 'Notification' in window;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** 'unsupported' | 'default' | 'granted' | 'denied' */
|
|
23
|
+
export function getPermission() {
|
|
24
|
+
return notificationsSupported() ? Notification.permission : 'unsupported';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function isMuted() {
|
|
28
|
+
return localStorage.getItem(MUTE_KEY) === '1';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function setMuted(muted) {
|
|
32
|
+
if (muted) localStorage.setItem(MUTE_KEY, '1');
|
|
33
|
+
else localStorage.removeItem(MUTE_KEY);
|
|
34
|
+
window.dispatchEvent(new CustomEvent(NOTIFICATION_PREF_EVENT));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** True only when a bubble should actually be shown right now. */
|
|
38
|
+
export function notificationsActive() {
|
|
39
|
+
return getPermission() === 'granted' && !isMuted();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Ask the browser for permission if it hasn't been decided yet.
|
|
44
|
+
* Returns the resulting permission string. No-op (returns current state) if
|
|
45
|
+
* already granted/denied or unsupported.
|
|
46
|
+
*/
|
|
47
|
+
export async function enableNotifications() {
|
|
48
|
+
if (!notificationsSupported()) return 'unsupported';
|
|
49
|
+
let perm = Notification.permission;
|
|
50
|
+
if (perm === 'default') {
|
|
51
|
+
try { perm = await Notification.requestPermission(); }
|
|
52
|
+
catch { perm = Notification.permission; }
|
|
53
|
+
}
|
|
54
|
+
window.dispatchEvent(new CustomEvent(NOTIFICATION_PREF_EVENT));
|
|
55
|
+
return perm;
|
|
56
|
+
}
|
|
@@ -4,6 +4,9 @@ import {
|
|
|
4
4
|
setTerminalFontSize, clearTerminalFontSize,
|
|
5
5
|
FONT_SIZE_MIN, FONT_SIZE_MAX, FONT_SIZE_EVENT,
|
|
6
6
|
} from '../terminal-font-prefs.js';
|
|
7
|
+
import {
|
|
8
|
+
getPermission, isMuted, setMuted, enableNotifications,
|
|
9
|
+
} from '../notification-prefs.js';
|
|
7
10
|
|
|
8
11
|
function renderDisplayPrefs() {
|
|
9
12
|
const current = getTerminalFontSize();
|
|
@@ -20,6 +23,55 @@ function renderDisplayPrefs() {
|
|
|
20
23
|
</div>`;
|
|
21
24
|
}
|
|
22
25
|
|
|
26
|
+
function renderNotificationPrefs() {
|
|
27
|
+
const perm = getPermission();
|
|
28
|
+
const muted = isMuted();
|
|
29
|
+
|
|
30
|
+
let status, control = '';
|
|
31
|
+
if (perm === 'unsupported') {
|
|
32
|
+
status = '<em>not supported</em>';
|
|
33
|
+
} else if (perm === 'denied') {
|
|
34
|
+
status = '<em>blocked</em>';
|
|
35
|
+
control = '<span class="sidebar-display-hint">Allow in browser site settings</span>';
|
|
36
|
+
} else if (perm === 'default') {
|
|
37
|
+
status = '<em>off</em>';
|
|
38
|
+
control = '<button class="sidebar-display-reset" data-action="enable-notifs">Enable</button>';
|
|
39
|
+
} else { // granted
|
|
40
|
+
status = muted ? 'muted' : 'on';
|
|
41
|
+
control = `<label class="sidebar-notif-mute"><input type="checkbox" data-action="mute-notifs"${muted ? ' checked' : ''}/> Mute</label>`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return `<div class="sidebar-display-prefs" data-notif-block>
|
|
45
|
+
<div class="sidebar-display-row">
|
|
46
|
+
<label class="sidebar-config-key">Desktop notifications</label>
|
|
47
|
+
<span class="sidebar-display-value" data-notif="status">${status}</span>
|
|
48
|
+
</div>
|
|
49
|
+
${control ? `<div class="sidebar-display-row">${control}</div>` : ''}
|
|
50
|
+
</div>`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function bindNotificationPrefs(body) {
|
|
54
|
+
const repaint = () => {
|
|
55
|
+
const block = body.querySelector('[data-notif-block]');
|
|
56
|
+
if (!block) return;
|
|
57
|
+
const tmp = document.createElement('div');
|
|
58
|
+
tmp.innerHTML = renderNotificationPrefs();
|
|
59
|
+
block.replaceWith(tmp.firstElementChild);
|
|
60
|
+
wire();
|
|
61
|
+
};
|
|
62
|
+
function wire() {
|
|
63
|
+
body.querySelector('[data-action="enable-notifs"]')?.addEventListener('click', async () => {
|
|
64
|
+
await enableNotifications();
|
|
65
|
+
repaint();
|
|
66
|
+
});
|
|
67
|
+
body.querySelector('[data-action="mute-notifs"]')?.addEventListener('change', (e) => {
|
|
68
|
+
setMuted(e.target.checked);
|
|
69
|
+
repaint();
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
wire();
|
|
73
|
+
}
|
|
74
|
+
|
|
23
75
|
function bindDisplayPrefs(body) {
|
|
24
76
|
const slider = body.querySelector('#termFontSize');
|
|
25
77
|
const valueEl = body.querySelector('[data-display="size"]');
|
|
@@ -54,11 +106,13 @@ export const configSection = {
|
|
|
54
106
|
else if (typeof value === 'object') display = `<code>${JSON.stringify(value)}</code>`;
|
|
55
107
|
return `<div class="sidebar-config-item"><span class="sidebar-config-key">${key}</span><span class="sidebar-config-val">${display}</span></div>`;
|
|
56
108
|
}).join('');
|
|
57
|
-
body.innerHTML = renderDisplayPrefs() + itemHtml;
|
|
109
|
+
body.innerHTML = renderDisplayPrefs() + renderNotificationPrefs() + itemHtml;
|
|
58
110
|
bindDisplayPrefs(body);
|
|
111
|
+
bindNotificationPrefs(body);
|
|
59
112
|
} catch (e) {
|
|
60
|
-
body.innerHTML = renderDisplayPrefs() + '<div class="sidebar-empty">Failed to load config</div>';
|
|
113
|
+
body.innerHTML = renderDisplayPrefs() + renderNotificationPrefs() + '<div class="sidebar-empty">Failed to load config</div>';
|
|
61
114
|
bindDisplayPrefs(body);
|
|
115
|
+
bindNotificationPrefs(body);
|
|
62
116
|
}
|
|
63
117
|
},
|
|
64
118
|
};
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
<!-- Phase 3: list panel accordion sections mount here -->
|
|
39
39
|
</div>
|
|
40
40
|
<div class="sidebar-footer">
|
|
41
|
+
<div class="sidebar-announcement-slot" id="sidebarAnnouncementBanner"></div>
|
|
41
42
|
<div class="instant-mode-banner" id="instantModeBanner" hidden>
|
|
42
43
|
<div class="instant-mode-banner-title">⚡ Instant voice mode</div>
|
|
43
44
|
<div class="instant-mode-banner-body">
|