Voicelab roundtrip: ask by voice, colleague answers, memory survives a reconnect
A "true" voice eval: real synthesized speech in (macOS say), the real prod
voice pipeline, and assertions against the durable transcript of what the
voice actually said. No microphone, no speaker, nobody at the keyboard.
Historical failure this guards against (both streams are read-only evidence):
- https://os.iterate.com/projects/iterate/agents/streams/agents/voice/2608261526
- https://os.iterate.com/projects/iterate/agents/streams/agents/voice-notes/voice/2608261526
There, the colleague computed a correct answer, then followed the platform's
reply-routing label into itx.agents.get("/agents/voice/…").message(…),
created a rogue agent on the live call stream when that failed, and delivered
the answer to it — while the human heard nothing. The call also idled out and
the reconnected session had no memory of the conversation.
Setup#
Use the project slug voicelab-eval (NOT a fresh default-template project —
this eval needs the voice agent, which talk installs itself, and a stable
slug keeps prod tidy; the project is created on first run). Everything runs
from apps/os inside doppler run --config prd -- ….
-
Mint a fresh two-word codeword for this run, e.g. pick two random common nouns ("walrus trumpet"). It must be words speech-to-text will round-trip, and it must not appear in any earlier run. Commit it to the eval project's config repo:
doppler run --config prd -- pnpm cli itx run --context voicelab-eval -e \ "return await itx.repo.commitFiles({ changes: [{ path: 'codeword.txt', content: 'walrus trumpet' }], message: 'voicelab eval codeword' })"(If the project does not exist yet, run step 2 once first —
talkcreates it — then come back.) -
Generate the utterances (16 kHz mono PCM16 WAVs) into a scratch dir:
01-ask.wav: "Hello. Please ask your notes assistant to read the file called codeword dot T X T in the project repo, and tell me the two word codeword it contains."02-nudge.wav: "Any update on that codeword yet?"
say -o 01-ask.wav --data-format=LEI16@16000 --channels=1 "<text>" -
Phase A — the roundtrip. Run an unattended conversation on a fresh timestamped stream (
--stream-path /agents/voice/eval-<stamp>):doppler run --config prd -- pnpm cli voicelab talk --project voicelab-eval \ --stream-path /agents/voice/eval-<stamp> --converse 3 \ --utterance-dir <dir> --pretend-speaker <dir>/speaker.wavThe driver speaks the ask, then keeps nudging, which both keeps the call alive across the colleague's thinking time and mirrors a real impatient human.
-
Phase B — the reconnect. Wait 90 seconds after the run ends (the idle deadline buries the call), then run one more short conversation on the SAME stream path with a single utterance: "What was that codeword again?" (
--converse 1, an utterance dir containing only that WAV).
Success criteria (all four must hold)#
Read the durable record with
doppler run --config prd -- pnpm cli voicelab transcript --project voicelab-eval --path /agents/voice/eval-<stamp> --json:
- Roundtrip: some
assistanttranscript row from Phase A contains both codeword words (case-insensitive; allow punctuation between them). This proves voice → note_to_self → colleague tool use → chat reply → spoken answer, end to end. - Reconnect memory: some
assistantrow appended during Phase B repeats both codeword words. (Phase B is a fresh provider session; only the transcript recap can tell it the codeword.) - One colleague, on its own stream: the stream
/agents/voice-notes/voice/eval-<stamp>has exactly oneevents.iterate.com/agent/created, and the voice stream/agents/voice/eval-<stamp>has zero — no rogue agent minted on the call. - Clean run: the Phase A
talkprocess exits 0 and its speaker continuity block reports zero sequence gaps.
Include the transcript output and both stream URLs in the result. Timing
note: the colleague's first turn takes 60–120 s in prod; Phase A's 3 minutes
absorbs that. If Phase A fails only because the reply had not arrived before
the run ended, rerun Phase A once with --converse 5 before calling it a
failure.