Skip to content

feat: agent-simulation support#2075

Merged
toubatbrian merged 15 commits into
mainfrom
sim/agent-simulation-support
Jul 24, 2026
Merged

feat: agent-simulation support#2075
toubatbrian merged 15 commits into
mainfrom
sim/agent-simulation-support

Conversation

@u9g

@u9g u9g commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Port of the Python 1.6.6 simulation surface so JS agents are simulatable:

  • JobContext.simulationContext() resolves the scenario dispatch from the job's lk.simulator.dispatch attribute
  • the job shuts down when the simulator participant (lk.simulator) leaves
  • defineAgent({ onSimulationEnd }) runs on the session host's finalizeSimulation request and can veto a pass via ctx.fail()
  • text simulations dynamically drop STT/TTS/VAD (including agent-level overrides), disable RoomIO audio, and explicitly disable audio recording
  • ScenarioGroup, SimulationRun, SimulationRun_Job, simulationJob, and client-side RemoteSession.finalizeSimulation() use the generated @livekit/protocol surface
  • FinalizeSimulationError preserves a generated userVerdict recorded before a callback failure while still propagating the callback error
  • both simulator attribute keys have one canonical public declaration in types.ts
  • getJobContext is imported at module scope; the reverse dependency is type-only, so no runtime cycle requires an inline dynamic import

Python source parity

The authoritative release is livekit-agents@1.6.6 (e731eac7c). Actual fetched source patches covered the simulation foundation #5992 (5d427a497), #6014 (82f02c73d), and #6018 (2d2adbef2); the consolidated simulation glue #5688 (e41b88082); follow-ups #6037 (272a2bb34), #6036 (07761a97e), #6053 (1e2405943), #6055 (a236c5320), #6061 (9e5be77d0), and #6069 (20397c8ec); and example coverage #6089 (52a9881be). The JS port uses the framework's existing AsyncLocal job-context and generated Buf classes; Python-only process/decorator machinery is not copied. As in Python 1.6.6, simulationJob is public but remains unset during finalize because the current finalize protocol carries the run ID and verdict, not a simulation-job payload.

Python's generic _send_request() currently has the same fail-then-throw client bug: it raises on response.error before finalize_simulation() can read user_verdict. Per the correctness-priority requirement, JS fixes this with a finalize-specific typed error rather than copying the bug; generic request error handling remains unchanged.

Focused simulation-protocol proof

QCLI cannot synthesize lk.simulator.dispatch, create a participant carrying lk.simulator, or issue the finalize-simulation remote protocol, so these focused tests are authoritative for those boundaries:

  • agents/src/job.test.ts: dispatch attribute decoding, malformed/missing dispatch behavior, and simulator-participant disconnect shutdown
  • agents/src/voice/remote_session.test.ts: a genuinely connected in-memory transport pair runs the public RemoteSession client against a real SessionHost; it proves normal finalize, callback-error propagation, fail-then-throw preservation on FinalizeSimulationError.userVerdict, and unchanged generic request errors
  • agents/src/voice/agent_session_text_only.test.ts: dynamic text/audio resolution, audio-model gating, and recordingOptions.audio = false
  • agents/src/simulation.test.ts: generated scenario/run/job exports and simulationJob lifecycle

Verification on 39349a8468c0f7519c0436c654d3c32ff36b06bf: 50 focused tests passed, core typecheck passed, scoped lint and formatting passed, and full pnpm build passed. The workspace-wide API check remains blocked by pre-existing tooling issues: plugins/liveavatar has no api-extractor.json, and core API Extractor 7.43 cannot parse the merged main entrypoint's export * as declaration.

Cue voice E2E

Secondary regression smoke only; this does not prove simulation finalization (the connected client-host tests above do).

  • Exact head: 39349a8468c0f7519c0436c654d3c32ff36b06bf
  • Session: sid_35821b106dc4, ordinary --mode voice
  • Predicate: user_input_transcribed(.is_final=true) -> agent_state_changed(.new_state="AS_SPEAKING") -> conversation_item_added(.item.message.role="ASSISTANT")[20s]
  • Result: resolved in 10,708 ms; final STT at 12,838 ms, full user turn committed at 13,137 ms, agent speaking at 14,016 ms, and assistant commit at 18,437 ms (I can hear you and will answer normally for the static import exact head voice session.)
  • Providers: LiveKit inference gateway with deepgram/nova-3 STT, openai/gpt-4.1-mini LLM, cartesia/sonic-3 TTS, and turn-detector-v1 EOT
  • Persisted artifacts: ~/.cue-cli/sessions/sid_35821b106dc4/recording.wav (stereo PCM, 48 kHz, 24.445 s) and recordings/002_run/recording.wav (stereo PCM, 48 kHz, 10.715 s), plus command/session JSON and the complete event stream

@u9g
u9g requested a review from a team as a code owner July 20, 2026 18:16
@changeset-bot

changeset-bot Bot commented Jul 20, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 39349a8

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 39 packages
Name Type
@livekit/agents Patch
@livekit/agents-plugin-anam Patch
@livekit/agents-plugin-anthropic Patch
@livekit/agents-plugin-assemblyai Patch
@livekit/agents-plugin-azure Patch
@livekit/agents-plugin-baseten Patch
@livekit/agents-plugin-bey Patch
@livekit/agents-plugin-cartesia Patch
@livekit/agents-plugin-cerebras Patch
@livekit/agents-plugin-deepgram Patch
@livekit/agents-plugin-did Patch
@livekit/agents-plugin-elevenlabs Patch
@livekit/agents-plugin-fishaudio Patch
@livekit/agents-plugin-google Patch
@livekit/agents-plugin-hedra Patch
@livekit/agents-plugin-hume Patch
@livekit/agents-plugin-inworld Patch
@livekit/agents-plugin-krisp Patch
@livekit/agents-plugin-lemonslice Patch
@livekit/agents-plugin-liveavatar Patch
@livekit/agents-plugin-livekit Patch
@livekit/agents-plugin-minimax Patch
@livekit/agents-plugin-mistral Patch
@livekit/agents-plugin-mistralai Patch
@livekit/agents-plugin-neuphonic Patch
@livekit/agents-plugin-openai Patch
@livekit/agents-plugin-perplexity Patch
@livekit/agents-plugin-phonic Patch
@livekit/agents-plugin-protoface Patch
@livekit/agents-plugin-resemble Patch
@livekit/agents-plugin-rime Patch
@livekit/agents-plugin-runway Patch
@livekit/agents-plugin-sarvam Patch
@livekit/agents-plugin-silero Patch
@livekit/agents-plugin-soniox Patch
@livekit/agents-plugin-tavus Patch
@livekit/agents-plugins-test Patch
@livekit/agents-plugin-trugen Patch
@livekit/agents-plugin-xai Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

devin-ai-integration[bot]

This comment was marked as resolved.

u9g and others added 6 commits July 20, 2026 18:34
….50.2

The protocol package now re-exports SimulationDispatch, Scenario, and
SimulationMode from its root, so drop the hand-rolled protojson decoding
in favor of SimulationDispatch.fromJsonString.
Preserve the simulation tests and adopt main's protocol dependency and telemetry coverage while resolving conflicts.

Co-authored-by: Cursor <cursoragent@cursor.com>
Preserve dynamic text simulation behavior and expose the generated protocol surface so the JS port matches Python 1.6.6, including finalize error and veto handling.

Co-authored-by: Cursor <cursoragent@cursor.com>
Expose a finalize-specific error carrying the agent verdict so callback failures remain visible without dropping a previously recorded veto.

Co-authored-by: Cursor <cursoragent@cursor.com>
Preserve the concurrent framework-info API alongside connected finalize transport coverage and typed veto errors.

Co-authored-by: Cursor <cursoragent@cursor.com>
devin-ai-integration[bot]

This comment was marked as resolved.

toubatbrian and others added 2 commits July 23, 2026 16:56
Keep both simulator attribute keys in the public types module so participant and dispatch handling cannot drift between duplicate declarations.

Co-authored-by: Cursor <cursoragent@cursor.com>
Use the module-scope job context import because the reverse dependency is type-only and does not create a runtime cycle.

Co-authored-by: Cursor <cursoragent@cursor.com>
@toubatbrian
toubatbrian merged commit 818db99 into main Jul 24, 2026
6 of 7 checks passed
@toubatbrian
toubatbrian deleted the sim/agent-simulation-support branch July 24, 2026 18:44
@github-actions github-actions Bot mentioned this pull request Jul 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants