Experiment: WhatsApp single-turn - #401
Draft
jptaylor wants to merge 22 commits into
Draft
Conversation
…acking - Lift event_context onto a top-level field on bus events so the TaskAgent LLM context never sees __event_context, recipient_ids, or recipient_reasons when an event falls through to its raw payload. - _extract_event_id reads the lifted field first, then falls back to the payload-embedded shape. - _record_event_order / _record_handled_event_order skip event.query so backfill envelopes don't pollute the live high-water mark. - _sort_ready_events keeps no-id events in arrival order instead of demoting them behind id-bearing events. - Add a _closed guard on EventRelay so a late inbound event after close() does not restart the worker. - Tests cover the no-leak path, the new sort stability, the event.query skip, and the top-level / fallback _extract_event_id shapes. Integration harness drains _game_event_queue after sending so post-handle assertions see synchronous state.
Move byoa_agent_name, _is_byoa_agent_name, _is_byoa_runner_name, _lookup_byoa_owner, and _byoa_wake_failure_message out of VoiceAgent into a new ByoaCoordinator collaborator at src/gradientbang/pipecat_server/byoa_coordinator.py. These five helpers have no shared state with the rest of VoiceAgent so they extract cleanly. Renamed to drop the redundant byoa_ prefix and underscore (now coordinator.agent_name_for / is_agent_name / is_runner_name / lookup_owner / wake_failure_message). VoiceAgent constructs ByoaCoordinator in __init__ and call sites go through self._byoa. Subsequent steps will move presence, broker auth, registry invalidation, and the wake flow.
Move _byoa_presence dict, _byoa_presence_sweep_task, _byoa_known_ships, the _ByoaPresence dataclass, the BYOA_PRESENCE_* constants, and the six presence-related methods (_on_byoa_presence, _push_byoa_presence, _ensure_byoa_presence_sweeper, _byoa_presence_sweep_loop, _mark_stale_byoa_presence_offline, _release_lock_on_byoa_offline) out of VoiceAgent into ByoaCoordinator. Adds one narrow host facade method on VoiceAgent: release_ship_lock, with an optional expected_task_id guard for race-free conditional pops. Adds coordinator.note_known_ship (called from _handle_start_task) and coordinator.close_sweeper (called from close_tasks). The coordinator still calls back to host._invalidate_byoa_registry_entry; that helper moves in step 4.
Move _byoa_active_agents dict and the BYOA-specific branch of _broker_identity_for_message out of VoiceAgent into ByoaCoordinator. Adds four coordinator methods: - register_active(name, ...) — adds to the active-agent table - deactivate(name) — pops, returns the prior ctx or None - get_active(name) — read-only lookup - resolve_identity(source, task_id) — the BYOA branch of broker auth, raises PermissionError on missing entry or task_id mismatch VoiceAgent._broker_identity_for_message now dispatches into coordinator.resolve_identity once is_agent_name confirms the source is BYOA. The in-process child fallback stays unchanged.
Move _invalidate_byoa_registry_entry from VoiceAgent into ByoaCoordinator.invalidate_registry_entry. VoiceAgent exposes a tiny get_agent_registry facade so the coordinator can reach into pipecat's private AgentRegistry state without crossing the host boundary elsewhere. Three call sites updated to self._byoa.invalidate_registry_entry; the two presence-internal call sites that previously went through self._host._invalidate_byoa_registry_entry now stay inside the coordinator.
Move the BYOA wake-flow subsystem into ByoaCoordinator: - _pending_wakes per-ship watchdog dict - _byoa_bus_channel (now _bus_channel) - _call_wake_agent / _dispatch_byoa_wake / _watch_wake_timeout - _cancel_pending_byoa_wake (now try_cancel_pending_wake) VoiceAgent gains three narrow host facade methods used only by the coordinator: ship_for_locked_task, clear_pending_task, has_pending_task. release_ship_lock (added in step 2) is now also used here. The dispatch block inside _handle_start_task still does the shared-state writes (_pending_tasks, _locked_ships) directly so the rollback path stays lexically local and readable; the watchdog spawn and wake-call dispatch go through coordinator.arm_wake_watchdog and coordinator.dispatch_wake_async respectively. on_agent_ready's BYOA-watchdog cancel becomes coordinator.cancel_pending_wake. close_tasks calls coordinator.cancel_all_pending_wakes. The coordinator's __init__ now takes a config kwarg (ByoaAgentConfig) so it can read agent_wake_timeout_seconds for the watchdog timer. VoiceAgent still keeps _byoa_config as its hello-handshake timeout source.
Rename _enqueue_deferred_update to enqueue_deferred_update and _update_polling_scope to update_polling_scope on VoiceAgent. The prior names were marked private but they're now part of the ByoaCoordinator host contract. The 16 internal callers in voice_agent.py and the test mocks are updated to match. No behavior change. Closes the byoa extraction series. VoiceAgent goes from 3,781 lines to ~3,000; ByoaCoordinator is the single owner of BYOA wake, presence, broker auth, and registry invalidation.
Post-extraction cleanup, no behavior change: - Expand file docstring from 4 to ~18 lines describing what VoiceAgent owns now that BYOA moved out: pipeline + speech cycle, deferred-update coalescer, tool handlers + bus broker, task lifecycle, TaskStateProvider, the narrow ByoaCoordinator host facade. - Fix self-contradicting _locked_ships comment (was both "server-side mutex is authoritative" and "the bot is the only authority" — only the latter is true post-DB-lock-removal). - Rename two mislabeled section headers: 'Lifecycle' → 'Speech-cycle gating', 'Agent lifecycle' → 'Subagent spawn handshake'. Both reflect what the methods under them actually do. - Add 'Host facade for ByoaCoordinator' section header and cluster all five facade methods (ship_for_locked_task, clear_pending_task, has_pending_task, release_ship_lock, get_agent_registry) together — the latter two were ~600 lines away from their siblings. - Move is_our_task up to the existing 'TaskStateProvider protocol' section where it belongs (EventRelay calls it, not the coordinator).
# Conflicts: # CHANGELOG.md
Carry garrison mode (defensive/offensive/toll) and active combat per visited sector in the local_map_region response so the client can color icons correctly on first render without waiting for follow-up events. - LocalMapSectorGarrison gains a mode field, populated for all visible garrisons (own and enemy). - New LocalMapSectorCombat type and combat field on LocalMapSector, populated from sector_contents.combat where ended is false. - Mirror both shape changes in the client types.
Consume the snapshot's new garrison.mode field and active combat per sector. Color garrison icons by mode (defensive=cyan, offensive=red, toll=gold) for own and enemy garrisons alike. Surface corp-mate ships in a distinct color from self-owned by threading owner_character_id through the ship payload and classifying in BigMapPanel / MiniMapPanel. Tighten sectorsEquivalentForRender to compare garrison.mode and combat — fixes the "icon doesn't update without hover" memo-skip bug. Server: owner_character_id added to buildShipPayload return so the client can do the self-vs-corp-mate classification.
Replace the fan-in of bespoke per-event setters (updateMapSectors,
addCombatSector, removeCombatSector) with a single discriminated MapDelta
union and one applyMapDelta merge function in mapSlice. Every map-relevant
server event in GameContext (sector.update, map.update, garrison.destroyed,
combat.round_waiting, combat.ended) now funnels through this one entry
point.
Combat overlay state moves from the slice-level combat_sectors Record onto
each sector node as `combat?: { combat_id }`, with BigMapPanel deriving
the combatSectorsSet from sector data. This removes the dual-pass
ignoreLocal + source-priority branching that grew up around the old
updateMapSectors, so future overlays only need one merge case instead of
new merge rules.
- Extract mapShipsForViewer helper; drop 3 copies of the same ship classification (Big/Mini/Mobile). - Drop redundant garrison node style (was identical to garrisonOffensive); offensive becomes the fallback for unknown modes. - Trim verbose comments on MapDelta and corpMateColor. - Pick up MobileMap into the new corp-mate coloring path (was still using the pre-PR shape).
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Preview deployed: https://gradient-bang-jjbbxwznl-daily-co.vercel.app |
jptaylor
added a commit
that referenced
this pull request
May 18, 2026
These files were accidentally squashed into #403; they belong only to the in-flight WhatsApp POC in #401. - Delete src/gradientbang/sms_agent/, prompts/agents/sms_agent.md, deployment/supabase/functions/sms_agent_tools/, docs/sms-whatsapp.md - Drop sms-agent extras and script entry from pyproject.toml - Remove get_sms_agent_llm_config() and build_sms_agent_prompt() - Refresh uv.lock (drops twilio + aiohttp-retry)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.