feat: couple broadcast to voice + voice-controlled broadcast toggle

Completes the STREAM_BROWSER=true behaviour:
- handleJoin auto-starts the broadcast on voice join and wires the session to
  the guild streamer; each turn reports the live state to the brain so search
  routes Chrome (live) vs Gemini (off).
- New setBroadcast tool lets the user toggle the broadcast by voice ("방송
  켜줘/꺼줘") via the LLM (no hardcoded phrases); it refuses when
  STREAM_BROWSER=false. The directive flows brain -> bridge (broadcast_action)
  -> bot streamer.start/stop, guarded by isActive() so it's idempotent.
- Per-turn IPC uses a thread-local (reply/turn_state.py) instead of threading
  params through the whole engine chain: bridge sets broadcasting in, tool
  records the directive out; Tool.execute exposes broadcasting on ToolContext.

Bot typecheck clean; brain covered by tests/test_set_broadcast.py (+ existing
routing tests). Specs + docs updated.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
javis-bot
2026-06-11 10:08:30 +09:00
parent 35e754d6ee
commit ca86390407
11 changed files with 316 additions and 14 deletions

View File

@@ -35,11 +35,12 @@ entry) and falls back to the master flag so behaviour is unchanged.
| `browseAndSearch` tool (true) | `src/jarvis/tools/builtin/browse_and_search.py` -> subprocess the Node core | TODO |
| Gemini search (false) | `realtime_search.py` `gemini_cli_search()` (oauth, CLI) / `gemini_search()` (apikey, REST) | done |
| Search routing by live broadcast | `web_search.py` uses `ToolContext.broadcasting` + master flag | done |
| Live broadcast state in tool ctx | `tools/base.py` `ToolContext.broadcasting` | done |
| Bridge passes broadcast state | bot `bridge.ts` request + `bridge/server.py` -> ToolContext | TODO |
| Auto-broadcast on voice join (true mode) | `bot/src/index.ts` `handleJoin()` starts streamer | TODO |
| Voice broadcast on/off toggle | brain tool -> reply directive -> bot `streamer.start/stop` | TODO |
| `/stream` + start refused (false mode) | `bot/src/index.ts` `handleStream()` already gated | done |
| Live broadcast state in tool ctx | `tools/base.py` `ToolContext.broadcasting` from `turn_state` | done |
| Per-turn state channel | `reply/turn_state.py` (thread-local: broadcasting in, directive out) | done |
| Bridge passes broadcast state | `bridge.ts` `?broadcasting=` + `server.py` `think(broadcasting=)` | done |
| Auto-broadcast on voice join (true mode) | `bot/src/index.ts` `handleJoin()` starts streamer + wires hooks | done |
| Voice broadcast on/off toggle | `setBroadcast` tool -> `broadcast_action` -> bot `streamer.start/stop` | done |
| `/stream` + start refused (false mode) | `handleStream()` gated + `setBroadcast` refuses | done |
| Specs + `docs/llm_contexts.md` | alongside each tool | done |
## Design decisions