Skip to content

Commit 8012c06

Browse files
author
Carsten
committed
fix(05.1-01): GREEN — session.type='realtime' + error-event handler in sideband.ts (defect qwibitai#6 L1 + Pitfall 2)
Layer-1 fix: add type:'realtime' discriminator to session.update payload at sideband.ts:619. OpenAI Realtime GA 2026 requires this field on every session.update. Placed FIRST in the object literal so extraSession spread can still override if a future caller explicitly needs a 'transcription' session (no production caller currently does). See 05.1-RESEARCH.md §2.4 + §6.1. Observability (Pitfall 2): add explicit 'error' case to the WS onmessage handler. Logs event='session_update_rejected' at ERROR level with code, message, param, error_type, openai_event_id. This closes the observability gap that let defect qwibitai#6 remain invisible during Plan 05-03 Task 5 live calls. Explicit return after log matches existing handler conventions. All Wave 3 prior work preserved (amd-classifier.ts, persona.ts, pre-greet.ts, cost-farewell path — unchanged). Full voice-bridge suite: 365 passed / 4 skipped. Sideband tests: 28/28 pass. TypeScript strict build clean. Closes defect qwibitai#6 Layer 1 (persona-swap silent failure). Layer 2 (synthetic user-directive injection in webhook.ts onHuman) follows in Task 3.
1 parent 88df156 commit 8012c06

1 file changed

Lines changed: 36 additions & 1 deletion

File tree

voice-bridge/src/sideband.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,34 @@ export function openSidebandSession(
526526
return
527527
}
528528

529+
// Plan 05.1-01 (Pitfall 2 observability): explicit OpenAI WS error
530+
// handler. Without this, invalid_request_error (e.g. missing
531+
// session.type) is swallowed silently and defects like #6 remain
532+
// invisible in logs for weeks. Log at ERROR level with full error
533+
// envelope so ops can grep session_update_rejected.
534+
// See .planning/phases/05.1-amd-persona-handoff-redesign-and-asr-upgrade/05.1-RESEARCH.md §8 Pitfall 2.
535+
if (parsed?.type === 'error') {
536+
const err =
537+
(parsed as {
538+
error?: {
539+
code?: string
540+
message?: string
541+
param?: string
542+
type?: string
543+
}
544+
})?.error ?? {}
545+
log.error({
546+
event: 'session_update_rejected',
547+
call_id: callId,
548+
code: err.code,
549+
message: err.message,
550+
param: err.param,
551+
error_type: err.type,
552+
openai_event_id: (parsed as { event_id?: string })?.event_id,
553+
})
554+
return
555+
}
556+
529557
// All other event types: silent ignore.
530558
} catch (e: unknown) {
531559
const err = e as Error
@@ -616,7 +644,14 @@ export function updateInstructions(
616644
})
617645
return false
618646
}
619-
const session: Record<string, unknown> = { ...extraSession, instructions }
647+
// Plan 05.1-01 (defect #6 L1): OpenAI Realtime GA 2026 requires the
648+
// session.type discriminator on every session.update. Without it the
649+
// server rejects with invalid_request_error(param='session.type') and the
650+
// persona swap silently fails. `type` is placed FIRST so extraSession
651+
// spread can still override it if a future caller explicitly needs a
652+
// 'transcription' session — no production caller currently does.
653+
// See .planning/phases/05.1-amd-persona-handoff-redesign-and-asr-upgrade/05.1-RESEARCH.md §2.4.
654+
const session: Record<string, unknown> = { type: 'realtime', ...extraSession, instructions }
620655
if ('tools' in session) {
621656
log.error({
622657
event: 'slow_brain_tools_field_stripped_BUG',

0 commit comments

Comments
 (0)