- tap_engine.rs: spawn_tap_engine(session_id, tap_url) — dials the brain,
runs TapClient with bounded exponential backoff (250ms→5s cap, infinite
retries). Cold-path network I/O on tokio's pool, NOT a timing thread
(spec §4.3 deviation boundary).
- session_map.rs: +tap_registry (DashMap<ChannelId, TapConn>); +default_tap_url.
drive_all_sessions observes Connected+tap.is_none() → spawn TapEngine, set
channel.tap = Some(TapHandle). Observes Closing+tap.is_some() → fire close
oneshot, abort task, clear tap BEFORE state advances to Closed.
- routes.rs: POST /v1/sessions accepts optional {"tap_url":...} body;
resolve_tap_url validates ws:// 127.0.0.1/localhost, rejects wss:// + non-
loopback ws:// + bad schemes with 400 Bad Request (fail-fast per spec §4.4).
- main.rs: reads RUTSTER_TAP_URL env (default ws://127.0.0.1:8081/echo).
- rtc_session.rs (rutster-media): pipe field changed from EchoAudioPipe to
Box<dyn AudioPipe + Send> — the seam test (§8.5 #6):
loop_driver's call sites (sink.on_pcm_frame / source.next_pcm_frame)
are byte-identical; only the field type widens. RtcSession::new keeps
EchoAudioPipe default (slice-1 tests unchanged); binary uses
RtcSession::set_pipe(tap_audio_pipe) to swap on the Connected transition.
- 4 unit tests for tap_url validation (loopback accepted, non-loopback
rejected, wss rejected fail-fast, default fallback).
Spec ref: 2026-06-28-slice-2-agent-tap-design.md §4.3, §4.4, §5.1, §7.
47 KiB
47 KiB