Skip to content

fix(web): use session flavor label in voice context formatters (closes #680)#815

Merged
tiann merged 1 commit into
tiann:mainfrom
heavygee:fix/voice-flavor-labels-680
Jun 8, 2026
Merged

fix(web): use session flavor label in voice context formatters (closes #680)#815
tiann merged 1 commit into
tiann:mainfrom
heavygee:fix/voice-flavor-labels-680

Conversation

@heavygee
Copy link
Copy Markdown
Collaborator

@heavygee heavygee commented Jun 6, 2026

Closes #680

Problem

web/src/realtime/hooks/contextFormatters.ts hardcodes the literal string "Claude Code" in four places that get fed into the voice agent's context window:

  • formatPlainText — assistant message prefix (Claude Code: <text>...)
  • formatPermissionRequest — permission prompt narration (Claude Code is requesting permission to use ...)
  • formatMessage tool-call narration — two places (Claude Code is using ${name} and Claude Code is using ${name} with arguments: ...)

Result: voice readouts always announce "Claude Code" regardless of which agent flavor (Cursor, Codex, Gemini, Kimi, OpenCode) is actually running the session. The voice context lies about which agent the user is talking about, which is confusing in a fleet view and just plain wrong on a single-flavor non-Claude session.

Fix

Thread agentLabel: string through the affected formatter signatures and resolve the label once per call in voiceHooks from session.metadata.flavor via getFlavorLabel() from @hapi/protocol:

function getAgentLabel(session: Session | null): string {
    const flavor = (session?.metadata as SessionMetadataSummary | undefined)?.flavor
    return isKnownFlavor(flavor) ? getFlavorLabel(flavor) : 'coding agent'
}

Falls back to the generic "coding agent" for unknown or missing flavors (still better than substituting "Claude Code" everywhere).

Files

  • web/src/realtime/hooks/contextFormatters.ts (+27/-18) — signature changes + literal swaps
  • web/src/realtime/hooks/contextFormatters.test.ts (+33/-9) — pass agentLabel, add regression test for bug(voice): context formatters hardcode "Claude Code" for all agent flavors #680 that asserts "Claude Code" never appears for non-Claude flavors
  • web/src/realtime/hooks/voiceContextPlan.ts (+6/-2) — thread agentLabel into buildSessionVoiceContextPlan and pass to its inner formatMessage call
  • web/src/realtime/hooks/voiceContextPlan.test.ts (+3/-2) — pass agentLabel arg, assert no "Claude Code" substitution
  • web/src/realtime/hooks/voiceHooks.ts (+19/-7) — getAgentLabel helper + thread through all 5 call sites (reportSession, onPermissionRequested, onMessages, prepareVoiceSession)

Net: 5 files, +88 / -38 lines.

Compatibility

  • formatReadyEvent already used a generic "coding agent" phrasing; no signature change.
  • getFlavorLabel/isKnownFlavor already exist in shared/src/flavors.ts (added in the voice/flavor work that landed earlier); only the threading is net-new here.
  • SessionMetadataSummary already has flavor?: string | null.
  • Unknown/missing flavor falls back to "coding agent" — strictly better than the current hardcoded "Claude Code".

Verification

  • bun x vitest run web/src/realtime/hooks/contextFormatters.test.ts — 15/15 passing, including new regression assertions that "Claude Code" is absent regardless of agentLabel.
  • bun x tsc --noEmit from web/: zero new errors introduced (pre-existing workspace-link errors unrelated to this change).
  • In-production on a multi-flavor (Cursor + Codex + Claude) fleet: voice context now correctly attributes messages to the actual agent flavor.

Made with Cursor

…tiann#680)

Replaces hardcoded \"Claude Code\" strings in voice context injections
with the active session's flavor label (Cursor, Codex, Gemini, etc.)
via getFlavorLabel() from @hapi/protocol. Falls back to \"coding agent\"
for unknown or missing flavors.

Threads an agentLabel string param through:

- formatMessage
- formatNewSingleMessage
- formatNewMessages
- formatHistory
- formatSessionFull
- formatPermissionRequest
- buildSessionVoiceContextPlan (passes to its internal formatMessage call)

voiceHooks adds a single getAgentLabel(session) helper that reads
session.metadata.flavor and resolves the display label once per call.

Tests updated to cover the new parameter shape and to assert that
\"Claude Code\" is never substituted regardless of agent flavor.

formatReadyEvent already used a generic \"coding agent\" phrasing and
needs no signature change.

Closes tiann#680

Co-authored-by: Cursor <cursoragent@cursor.com>
Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Findings
No issues found in the added or modified lines.

Questions
None.

Summary
Review mode: initial
Reviewed the full diff for the voice context formatter label threading. The formatter signatures, voice hook call sites, and bootstrap context plan all consistently pass the resolved agent label, and the touched tests cover the main non-Claude regression path. Residual risk: I could not run the test suite in this runner because bun is not installed.

Testing
Not run locally: bun unavailable in this environment (bun: command not found).

HAPI Bot

@tiann tiann merged commit 9349acb into tiann:main Jun 8, 2026
2 checks passed
@heavygee heavygee deleted the fix/voice-flavor-labels-680 branch June 8, 2026 08:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(voice): context formatters hardcode "Claude Code" for all agent flavors

2 participants