feat(canvas-sync): derive voiceCapable + voiceId from agent_config.settings.voice#1330
Merged
Merged
Conversation
…ttings.voice Until now `voiceCapable` was a phantom field — defined on the cloud's AgentPresence shape, read by the 🎤 header badge + agents-panel badge, but never produced. The cloud canvas API returned agent shapes with no such field, so every read fell to `?? false` and the badge was dead code. Truth source already exists: `agent_config.settings.voice` (kokoro voice ID, see canvas-interactive.ts hashTts + assignment.ts). Same pattern as identityColor injection in syncCanvas — extend the SQL filter to also match rows with `voice` in settings, then surface `voiceCapable: true` + `voiceId` on the agent shape pushed to cloud. Cloud passes the payload through unchanged (host-canvas-state-routes.ts spreads row.payload), so this single node-side change makes the voiceCapable boolean real end-to-end. Unblocks the speak V0 LocalActionRow lane (kai msg-1777913827161, re-scoped 1777914293792 to fix upstream first). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3 tasks
itskai-dev
added a commit
that referenced
this pull request
May 4, 2026
…ud (#1331) * fix(canvas-sync): mirror voiceCapable+voiceId injection in pushCanvasStateToCloud Two node writers POST to /api/hosts/:id/canvas: - src/cloud.ts:1558 syncCanvas (30s cadence) — sets voiceCapable via PR #1330 truth seam from agent_config.settings.voice - src/server.ts:13151 pushCanvasStateToCloud (5s cadence) — sent only {state, task, displayName, identityColor, avatar} The cloud POST handler does `payload: agentState as object` (full replace, not merge). The 5s writer was clobbering voiceCapable/voiceId set by the 30s syncCanvas. Compass observed flipping voiceCapable true→false→true on each cycle. Patch: extend the 5s writer to mirror writer #1's voice derivation from the same agent_config.settings.voice source. Both writers now agree on payload shape so the later one preserves what the earlier set. Unblocks reflectt-cloud PR #3230 speak V0 proof. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(scope-policy): add task tag for src/server.ts sensitive-path gate task-1777140469973-770cy4yma — speak visibility V0 truth-seam follow-up unblocking pushCanvasStateToCloud writer #2. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Summary
voiceCapablewas a phantom — defined onAgentPresence(cloud), read by the 🎤 header badge + agents-panel badge + my new Speak row inAgentDetailPanel.tsx, but never produced anywhere. Every consumer fell to?? falseand the badge was dead code.agent_config.settings.voice(kokoro voice ID — seecanvas-interactive.tshashTts andassignment.ts). Same shape asidentityColor/avatarinjection.syncCanvasto also include rows withvoicein settings, then injectsvoiceCapable: true+voiceIdon the agent shape pushed to cloud.host-canvas-state-routes.tsspreadsrow.payload), so this single node-side change makes the boolean real end-to-end.Why this is a 1-file change
Cloud is already pass-through.
apps/web/src/app/presence/use-host-state.ts:340,464already mapsvoiceCapable: a.voiceCapable ?? falseandvoiceId: a.voiceId. The whole chain was waiting on the producer.Verified canonical baseline
Curled
/api/hosts/e4e35463…/agent-configs— compass hassettings.voice = "af_sarah". Once this rolls, compass will be the lit-row test case for the speak V0 lane.Test plan
npx tsc --noEmit)/api/hosts/.../canvaspost-roll — confirmagents.compass.voiceCapable === true+voiceId === "af_sarah"🤖 Generated with Claude Code