Skip to content

feat: attended transfer via REFER + Replaces#47

Merged
wavekat-eason merged 2 commits into
mainfrom
feat/attended-transfer
Jun 30, 2026
Merged

feat: attended transfer via REFER + Replaces#47
wavekat-eason merged 2 commits into
mainfrom
feat/attended-transfer

Conversation

@wavekat-eason

Copy link
Copy Markdown
Contributor

What

Adds attended (consultative) transfer to the engine — the follow-up to blind transfer (#45). It's the same in-dialog REFER, but the Refer-To embeds a Replaces header (RFC 3891) naming a consultation dialog the transferor already established to the target. The transferee's INVITE then replaces that leg rather than ringing the target afresh, so the held party and the consulted target are connected on the call the target already accepted.

API added

  • Call::attended_transfer(target, &DialogTriplet) — mirrors blind_transfer; outcome arrives identically over NOTIFY/sipfrag on inbound_requests, so a consumer drives blind and attended the same way.
  • refer::refer_to_with_replaces(target, &DialogTriplet) — builds the Refer-To with a percent-escaped Replaces, mapping the consult dialog's remote_tag/local_tag into the target's to-tag/from-tag frame (the RFC 3891 dialog-matching swap).
  • refer::DialogTriplet + Call::dialog_triplet() — expose a call's dialog identity (Call-ID + tags) so the consultation leg can be named.

Tests

  • refer_to_with_replaces_embeds_escaped_dialog + escape_leaves_unreserved_untouched — header construction and escaping.
  • attended_refer_carries_replaces — UA-level: drives the REFER and asserts the escaped Replaces rides the wire.
  • Full suite green (214 unit + integration), clippy clean, fmt clean.

docs/RFC-COVERAGE.md updated — RFC 3891 moves from "Not implemented" to Implemented.

Notes

  • No Referred-By (RFC 3892) and no acting-on-inbound-REFER — unchanged from blind, left to the consumer.
  • Consumed by wavekat-voice's attended-transfer feature; that PR patches this branch locally and will pin the released version once this lands and release-plz publishes.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Y59X1MtJu4rYWTDLGYB2KU

wavekat-eason and others added 2 commits June 29, 2026 22:02
Add `Call::attended_transfer` — the same in-dialog REFER as blind
transfer, but its Refer-To embeds a Replaces header (RFC 3891) naming a
consultation dialog the transferor already built to the target, so the
transferee takes over that leg instead of ringing the target afresh.

- `refer::refer_to_with_replaces` builds the Refer-To with a
  percent-escaped Replaces, mapping the consult dialog's remote/local
  tags into the target's to-tag/from-tag frame.
- `refer::DialogTriplet` + `Call::dialog_triplet` expose a call's dialog
  identity so the consult leg can be named.
- Progress is reported identically to blind (sipfrag NOTIFY), so a
  consumer drives both the same way.

Tests: header building + escaping (unit), and a UA-level REFER that
asserts the escaped Replaces rides the wire. RFC-COVERAGE updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y59X1MtJu4rYWTDLGYB2KU
The attended transfer Refer-To should target the consultation peer's
remote target (its Contact), per RFC 5589 §7 — paired with the dialog's
Replaces. Cache it on the Call at establishment and expose a cheap
accessor, so the consumer reads it without locking the dialog.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y59X1MtJu4rYWTDLGYB2KU
@wavekat-eason
wavekat-eason merged commit 2e878bc into main Jun 30, 2026
1 check passed
@wavekat-eason
wavekat-eason deleted the feat/attended-transfer branch June 30, 2026 10:23
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.

1 participant