Skip to content

VAPI-3507 stream available and unavailable events#5

Merged
smoghe-bw merged 26 commits into
mainfrom
metadata-for-stream-events
Jul 22, 2026
Merged

VAPI-3507 stream available and unavailable events#5
smoghe-bw merged 26 commits into
mainfrom
metadata-for-stream-events

Conversation

@smoghe-bw

Copy link
Copy Markdown
Contributor

No description provided.

@smoghe-bw
smoghe-bw requested review from a team as code owners April 27, 2026 13:29
@bwappsec

bwappsec commented Apr 27, 2026

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues
Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

smoghe-bw added 3 commits April 27, 2026 10:29
Emit connectStatus event separately from ready so callers can
subscribe to connect status updates independently via onConnectStatus.
@smoghe-bw smoghe-bw changed the title feat: add connectStatus to readyMetadata on connectStatus event VAPI-2525 feat: add connectStatus to readyMetadata on connectStatus event Apr 27, 2026
smoghe-bw and others added 8 commits June 2, 2026 09:25
…reamAvailable callback

- RtcStream.mediaStream and callId are now optional: notification fires first
  with callId (no mediaStream) for call-arrival UI, then again via WebRTC
  ontrack with mediaStream when audio flows
- signaling.ts subscribes to streamAvailable/streamUnavailable WS events and
  emits them for BandwidthRtc to forward to the app's handler
- Adds acceptStream(callId?) and declineStream(callId?) on BandwidthRtc and
  Signaling so apps can explicitly accept or decline an inbound stream
- Non-null assertions on setMicEnabled/setCameraEnabled/unpublish callers that
  always pass publish()-originated streams

Co-Authored-By: Claude Sonnet 4.6 (1M context) <[email protected]>
Delegates to v1 BandwidthRtcV1.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <[email protected]>
Without an explicit types entry, CRA's compiler couldn't resolve
declaration files from the dist/ directory via the npm link symlink.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <[email protected]>
…consumers

When installed via file: reference, CRA's source-map-loader follows relative
paths in .js.map files that escape the project root through the symlink,
causing ENOENT. Disabling source maps removes the .map files entirely.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <[email protected]>
Backwards compatibility shim: if the app never calls onStreamAvailable,
the gateway's new closed-gate-between-calls behavior would silently break
audio on all calls after the first. Auto-accepting keeps existing apps
working without any code change on their side.

Apps that register onStreamAvailable get the new ring/accept UX and are
responsible for calling acceptStream explicitly.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <[email protected]>
Gateway now opens the EgressGate at streamAvailable send time, so old SDKs
that never call acceptStream are unaffected. The SDK-side shim is no longer
needed.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <[email protected]>
Dead code from before the peerType-based offerSdp was adopted.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <[email protected]>
@smoghe-bw smoghe-bw changed the title VAPI-2525 feat: add connectStatus to readyMetadata on connectStatus event stream available and unavailable events Jun 2, 2026
smoghe-bw and others added 9 commits June 2, 2026 10:48
Build was blocked by a prettier check on the tsconfig. No logic changes;
prettier --write reformatted the file so npm run build can proceed.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <[email protected]>
…rences

Defaults to true. When true the gateway immediately re-opens the egress gate
after each call ends, so the next call's audio flows without any round-trip
delay. Pass false to restore legacy behaviour (gate stays closed until
streamAvailable is processed by the gateway).

Co-Authored-By: Claude Sonnet 4.6 (1M context) <[email protected]>
…mNotification callback

Previously the signaling-layer streamAvailable WS event was routed through
onStreamAvailable, which fires before the WebRTC ontrack event arrives and
therefore has no mediaStream. This broke existing consumers who assume
onStreamAvailable always carries a populated mediaStream.

Introduce onInboundStreamNotification as the dedicated callback for the
pre-media gateway notification (carries callId/autoAccepted, no mediaStream).
onStreamAvailable continues to fire only from the WebRTC ontrack handler,
so mediaStream is always present and existing consumers are unaffected.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <[email protected]>
…undStreamNotification callback"

This reverts commit e587b4d.
…ble single event

Previously the SDK fired onStreamAvailable twice per call: once from the WebRTC
ontrack event (with mediaStream only) and once from the WS streamAvailable
notification (with callId/autoAccepted but no mediaStream). This broke existing
apps that expected a single event with mediaStream always set.

Now the SDK holds the subscribe MediaStream from ontrack internally and only fires
onStreamAvailable once the WS notification arrives, delivering a combined event with
mediaStream, callId, and autoAccepted all present. ontrack ordering is handled in
both directions (stream arrives before or after the WS notification).

onStreamUnavailable is similarly fired from the WS streamUnavailable notification
using the stored stream, with onremovetrack as a fallback for older gateways.
A callIsActive guard prevents double-firing when both fire.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…anges

Keep both: WS streamAvailable/streamUnavailable correlation handlers and
acceptStream/declineStream from this branch, plus retryIceOnFailed from main.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@smoghe-bw smoghe-bw changed the title stream available and unavailable events VAPI-3239 stream available and unavailable events Jul 6, 2026
The gateway now negotiates a fresh subscribe track per call (unique msid) and
sends WS streamAvailable/streamUnavailable per call, instead of one persistent
track negotiated at connect. The correlation logic assumed ontrack fired once at
connect, which broke across consecutive calls:

- pendingSubscribeStream was never cleared at call end, so a later call whose WS
  streamAvailable arrived before its ontrack fired onStreamAvailable with the
  stale previous-call stream. Clear it on both call-end paths.
- The WS streamAvailable handler didn't check callIsActive, so if the onunmute
  fallback already fired, WS fired onStreamAvailable a second time. Guard it.
- onremovetrack leaked streamTracks entries when WS streamUnavailable fired
  first (per-call streams accumulate). Always delete the entry; fire only if the
  WS path hasn't.

onStreamAvailable/onStreamUnavailable now fire exactly once per call with
callId/autoAccepted metadata, and the onunmute/onremovetrack fallbacks still
cover gateways that don't send the WS events.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
smoghe-bw and others added 3 commits July 16, 2026 10:41
…s accept/decline

BREAKING CHANGE: inbound stream handling is reworked; intended as the 1.0.0 cutover.

The gateway no longer sends separate streamAvailable/streamUnavailable RPCs.
It rides caller identity (from/fromType/autoAccepted/tags) on the subscribe
sdpOffer that adds a call's per-call track, keyed by track id, and the SDK
derives stream available/unavailable from the offer's m-section changes
(ontrack / onremovetrack).

- types: SubscribeSdpOffer.trackMetadata (was streamSourceMetadata) keyed by
  trackId; add TrackMetadata; RtcStream gains from/fromType/tags and drops the
  internal callId.
- signaling/core: drop the streamAvailable/streamUnavailable WS listeners and the
  WS/ontrack correlation machinery; stash trackMetadata from handleSubscribeSdpOffer
  and attach it to onStreamAvailable via track-id lookup on ontrack.
- accept/decline are first-class again: with autoAccept off the gateway parks the
  call (egress gate closed, ringing) and the app prompts, then acceptStream opens
  the gate (audio flows) or declineStream cancels the call. No callId args — the
  gateway acts on the current per-call track.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@smoghe-bw
smoghe-bw requested review from a team as code owners July 22, 2026 16:04
@smoghe-bw smoghe-bw closed this Jul 22, 2026
@smoghe-bw smoghe-bw reopened this Jul 22, 2026
@smoghe-bw smoghe-bw changed the title VAPI-3239 stream available and unavailable events VAPI-3507 stream available and unavailable events Jul 22, 2026
@smoghe-bw
smoghe-bw merged commit 87df301 into main Jul 22, 2026
5 checks passed
@smoghe-bw
smoghe-bw deleted the metadata-for-stream-events branch July 22, 2026 18:40
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.

3 participants