Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
c8af6f5
feat(browser): run calls end to end on the REST signaling surface
purpshell Sep 5, 2026
97d36ca
feat(react): the complete calls UI over CallsController
purpshell Sep 5, 2026
3302c2b
feat(sdk): voip token resource and the client-rules shape the runtime…
purpshell Sep 5, 2026
291e58f
chore(contracts): refresh to polymorfa@d2ddaef83 and record calls cov…
purpshell Sep 5, 2026
08b3900
feat(calls): socket transport, renegotiation, resumption, and pod-los…
purpshell Sep 6, 2026
6c0f320
fix(calls): review follow-ups — socket connect lifecycle, media acqui…
purpshell Sep 6, 2026
ba846b6
fix(calls): track the whole socket attempt so close() settles during …
purpshell Sep 6, 2026
2f12809
test(calls): type the pending socket ticket in the close-during-acqui…
purpshell Sep 6, 2026
66e971b
test(calls): make the fake timers observe cancellation
purpshell Sep 6, 2026
6734154
test(calls): re-check timer cancellation inside fire()
purpshell Sep 6, 2026
82c2ada
chore(contracts): refresh to polymorfa@b413f2c6a
purpshell Sep 6, 2026
f7d8e85
fix(calls): address CodeRabbit full review — outbound placement, medi…
purpshell Sep 6, 2026
6a0985e
test(calls): cover the mute applied when media finishes opening
purpshell Sep 6, 2026
ca479d5
fix(calls): second review round — timer binding, negotiation rollback…
purpshell Sep 6, 2026
8c76a5d
fix(calls): label the dial pad erase button for what it does
purpshell Sep 6, 2026
a7033d8
fix(calls): reset the pre-answer toggles per call, forward the placem…
purpshell Sep 6, 2026
b609ef4
fix(calls): surface socket errors, guard Enter re-dial, scope the liv…
purpshell Sep 6, 2026
f0eeb45
fix(calls): scope the terminal-offer mapping, clear timers on a lifec…
purpshell Sep 6, 2026
4928ce1
fix(calls): recover to the interrupted status, fix ticket session and…
purpshell Sep 6, 2026
2939513
fix(calls): put the device preference back when the switch fails
purpshell Sep 6, 2026
d34dbfc
fix(calls): drop a late video upgrade, gate the element's mute control
purpshell Sep 6, 2026
9783442
fix(calls): localize the default device option
purpshell Sep 6, 2026
51b4a02
fix(calls): guard concurrent device switches and post-await transitions
purpshell Sep 6, 2026
405ef71
fix(calls): serialize same-kind device switches in the media session
purpshell Sep 6, 2026
604e8a2
docs(calls): qualify the 410/503 mapping to the media offer
purpshell Sep 6, 2026
f1ca4f1
fix(calls): keep the call duration running through a reconnect
purpshell Sep 6, 2026
981c9c7
feat(calls): let pmfa-call upgrade an audio call to video
purpshell Sep 6, 2026
9f8d813
fix(calls): require the ticket session, pin the clock in the duration…
purpshell Sep 6, 2026
31c949e
fix(calls): carry the line through the socket, gate the upgrade, hold…
purpshell Sep 6, 2026
75eb23a
fix(calls): correct how a failed placement surfaces, and stop announc…
purpshell Sep 6, 2026
639ec30
fix(calls): guard switchDevice before the preference write, fix two w…
purpshell Sep 6, 2026
fc37d9e
fix(calls): absorb answer and reject rejections in pmfa-call
purpshell Sep 6, 2026
fb224b0
fix(calls): reject a late backend connect, serialize the video upgrade
purpshell Sep 6, 2026
c56636b
fix(calls): absorb a dial against a disposed controller
purpshell Sep 6, 2026
007bfc3
fix(calls): absorb a hang-up against a disposed controller
purpshell Sep 6, 2026
0ddbe75
fix(calls): omit enableVideo and restartIce when signaling cannot ren…
purpshell Sep 6, 2026
e8fe3b6
feat(calls): heartbeat the calls socket
purpshell Sep 6, 2026
1aa1746
fix(calls): widen the terminal guard, absorb a disposed-controller se…
purpshell Sep 6, 2026
d93571a
fix(calls): guard every synchronous controller call from the UI surfaces
purpshell Sep 6, 2026
3464183
fix(calls): release the capture on a trackless acquisition, guard ena…
purpshell Sep 6, 2026
c390b7e
fix(calls): guard #connection after the call ends, gate the camera up…
purpshell Sep 6, 2026
b6c90c3
fix(calls): ignore a duplicate ended for an ended call; reattach enab…
purpshell Sep 6, 2026
eaf0e2c
fix(calls): make the ringing line a parameterised message
purpshell Sep 6, 2026
271bda6
fix(browser): bound the calls socket handshake with openTimeoutMs
purpshell Sep 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,39 @@
# Changelog

## Unreleased

- Added `CallsSocket` (the calls WebSocket: pushed `call.*` lifecycle events,
ICE both ways, ticket-based auth, capped reconnect) as a drop-in lifecycle
source for `createSignalingCallsBackend` and a `candidateTransport` for
`WebRtcMediaFactory`; `CallsSignalingClient` gained `renegotiate`,
`socketTicket`, and `socketUrl`; `BrowserTransport` exposes `baseUrl`.
- `CallsController` gained `enableVideo()` (audio→video upgrade by re-offer),
a `reconnecting` status with ICE restart and a bounded resumption window,
and ends the call as `pod_lost` / `capacity` when the offer is refused
with 410 / 503. The React camera button upgrades audio calls to video;
`CallStage` and `pmfa-call` show "Reconnecting…".
- Added `MessagingClient.voip.socketTicket` and `voip.agentToken`; typed
`maxSetupsPerMinute` in the client rules; refreshed the contracts and the
coverage ledger to polymorfa@83b819dd6 (renegotiate, ws-ticket,
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
agent-token).

- Added `MessagingClient.voip.token` for minting the browser call token, typed
the client-rules response the runtime actually returns (including the calls
bindings `maxConcurrency` and `allowedNumber`), and typed client actions.
- Added `createSignalingCallsBackend`, `IncomingCallRelay`, and
`incomingCallFromWebhook` so browser calls run end to end on the REST
signaling surface; `CallsController` now carries the calling `line` and
`capabilities`, device lists and selections, live device switching, and
`connectedAt`.
- Replaced the placeholder `CallSurface` with the complete call UI —
`IncomingCallCard`, `CallStage`, `CallControls`, `DialPad`, a pop-out call
window, and `useCallDuration` — themed from the shared appearance variables
and localized through new `calls.*` messages; `pmfa-call` follows the same
controller contract.
- Refreshed the Messaging and Platform contracts and the coverage ledger for
the `/api/voip/*` signaling routes and the `call.ended` / `call.telemetry`
webhook events.

## 0.1.0-dev.0 - 2026-08-19

- Added handwritten Messaging clients for sessions, messages, and webhooks.
Expand Down
38 changes: 28 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ The development branch contains the TypeScript server SDK, a framework-neutral
browser runtime, shared UI contracts, Web Components, React bindings, thin
Next.js server helpers, and a production-gated developer assistant. It follows
the Messaging and Platform contracts recorded at source revision
`f156af2dda13e62b6b106a542fdedb39524bdb66`. Graph-compatible APIs are outside
`d2ddaef836762dc189887f4f21b17c74c9c79ee7`. Graph-compatible APIs are outside
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
this SDK's initial scope.

## Package architecture
Expand Down Expand Up @@ -84,6 +84,7 @@ The handwritten Messaging resources in this milestone are:
- `business`: manage the connected Business App profile, commerce catalog,
products, collections, orders, compliance, linked accounts, and eligibility
- `calls`: reject an identified incoming Linked Device call
- `voip`: mint the browser call token used by `@polymorfa/browser` signaling
- `campaigns`: list, create, retrieve, inspect analytics, launch, pause, resume,
stop, and requeue project campaigns through the Messaging control plane
- `messages`: send every contract-defined message kind through one typed send
Expand Down Expand Up @@ -338,15 +339,32 @@ that interoperate with the Custom Elements standard. `@polymorfa/react`
provides idiomatic hooks and components, including controlled and SDK-owned
controller lifecycles.

### Calls boundary

Call media and signaling follow the implementation in `voip-v2`: browser WebRTC
media, offer/answer exchange, trickle ICE candidate submission and polling, and
the existing `/api/voip/calls/{id}` signaling paths. The application supplies a
`CallsBackend` for place, answer, reject, hangup, and lifecycle events because
that control plane is not exposed by the current `voip-v2` REST signaling
surface. The SDK does not invent group rooms, participants, reactions, hand
raising, or waiting rooms.
### Calls

Call media and signaling follow the platform's browser signaling surface:
WebRTC media, offer/answer exchange, trickle ICE candidate submission and
polling, and idempotent teardown over the `/api/voip/calls/{id}` paths, all on
the client token. `createSignalingCallsBackend` is the `CallsBackend` for that
surface; `IncomingCallRelay` is where the application hands the browser the
`call.received` webhook it received on its own server
(`incomingCallFromWebhook` maps the payload). The server mints the browser
token with `MessagingClient.voip.token` after granting the session's client
rules the `voip_place`, `voip_answer`, and `voip_signal` actions; those rules
also bind calls per token (`maxConcurrency`, `allowedNumber`).

Calls carry a `line`: `linkedDevice` (a paired WhatsApp device session, audio
and video) or `cloudApi` (the WhatsApp Business Calling API, audio only). Every
component gates on the snapshot's `capabilities`, never on the line name. The
controller also owns capture/playback device choice (`setPreferredDevices`,
`switchDevice`, `refreshDevices`) so a microphone or camera swap mid-call is a
track replacement, not a renegotiation. The SDK does not invent group rooms,
participants, reactions, hand raising, or waiting rooms.

`@polymorfa/react` ships the complete call UI: `CallSurface` (incoming card,
stage, control dock, and a pop-out window), plus `IncomingCallCard`,
`CallStage`, `CallControls`, and `DialPad` for composition. The design mirrors
the official WhatsApp desktop call windows in a monochrome Material-3 voice;
colors derive from the shared appearance variables.

## Next.js and dev mode

Expand Down
Loading