Skip to content

Add simulation glue#1733

Open
rosetta-livekit-bot[bot] wants to merge 1 commit into
mainfrom
seeps-crumbly-wiser
Open

Add simulation glue#1733
rosetta-livekit-bot[bot] wants to merge 1 commit into
mainfrom
seeps-crumbly-wiser

Conversation

@rosetta-livekit-bot

@rosetta-livekit-bot rosetta-livekit-bot Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add SimulationContext, SimulationVerdict, and simulation dispatch metadata parsing
  • wire optional defineAgent onSimulationEnd callbacks into job contexts
  • add RemoteSession updateIo/finalizeSimulation client methods and host-side finalize callback handling

Testing

  • pnpm --filter @livekit/agents build
  • pnpm --filter @livekit/agents lint (passes with existing warnings)
  • git diff --check

Note: pnpm --filter @livekit/agents api:check is currently blocked by the existing API Extractor limitation on export * as syntax in dist/index.d.ts.


Ported from livekit/agents#5688

Original PR description

No description.

@changeset-bot

changeset-bot Bot commented Jun 9, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 9250272

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

This PR includes changesets to release 34 packages
Name Type
@livekit/agents Patch
@livekit/agents-plugin-anam Patch
@livekit/agents-plugin-assemblyai 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-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-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-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

@rosetta-livekit-bot rosetta-livekit-bot Bot requested a review from theomonnom June 9, 2026 02:41

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 3 potential issues.

View 2 additional findings in Devin Review.

Open in Devin Review

Comment on lines +1337 to +1340
output: new pb.SessionRequest_UpdateIO_Output({
audioEnabled: outputAudioEnabled,
transcriptionEnabled: outputTranscriptionEnabled,
}),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 updateIo always sends an output sub-message even when no output fields are specified

In RemoteSession.updateIo, the output field is unconditionally constructed as new pb.SessionRequest_UpdateIO_Output(...) even when both outputAudioEnabled and outputTranscriptionEnabled are undefined. This is asymmetric with the input field handling (remote_session.ts:1333-1336), which conditionally creates the object only when inputAudioEnabled is defined. When updateIo({ inputAudioEnabled: true }) is called (only intending to change input), an empty output sub-message is still serialized on the wire. The handler at remote_session.ts:975 checks if (update.output), which will be truthy for the empty sub-message. While the inner field-level !== undefined guards currently prevent unintended changes for proto3 optional fields, if those fields were ever non-optional bool fields, undefined would default to false causing the handler to incorrectly disable audio and transcription output.

Suggested change
output: new pb.SessionRequest_UpdateIO_Output({
audioEnabled: outputAudioEnabled,
transcriptionEnabled: outputTranscriptionEnabled,
}),
output:
outputAudioEnabled === undefined && outputTranscriptionEnabled === undefined
? undefined
: new pb.SessionRequest_UpdateIO_Output({
audioEnabled: outputAudioEnabled,
transcriptionEnabled: outputTranscriptionEnabled,
}),
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +886 to +889
const dynamicRequest = req.request as { case?: string; value?: unknown };
if (dynamicRequest.case === 'finalizeSimulation') {
return this.handleFinalizeSimulation(req.requestId, dynamicRequest.value);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚩 Dynamic proto casting for finalizeSimulation bypasses type safety

Both handleFinalizeSimulation (remote_session.ts:886-888) and the RemoteSession.finalizeSimulation sender (remote_session.ts:1366-1369) use as unknown as casts to inject a finalizeSimulation case into the proto oneof fields. This suggests the proto definition (@livekit/protocol) does not yet include finalizeSimulation in SessionRequest.request or SessionResponse.response. This pattern works at runtime only if the proto serialization library preserves unknown/untyped oneof cases through toBinary()/fromBinary() round-trips — which @bufbuild/protobuf typically does NOT do for unknown oneof variants. If the transport serializes messages (as RoomSessionTransport does via msg.toBinary()), the finalizeSimulation field may be silently dropped. This would only work correctly with TcpSessionTransport or in-memory transports that don't go through protobuf binary serialization. Worth verifying against the actual proto definitions and testing with RoomSessionTransport.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread agents/src/job.ts
Comment on lines +199 to +201
const jobWithMetadata = this.#info.job as proto.Job & { metadata?: string };
const roomWithMetadata = this.#room as Room & { metadata?: string };
const metadata = jobWithMetadata.metadata || roomWithMetadata.metadata;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚩 simulationContext() uses metadata field accessed via type assertion

At job.ts:199-201, both this.#info.job and this.#room are cast to types with { metadata?: string } to access a metadata property that may not be in the TypeScript type definitions. This implies either (a) the proto Job type's metadata field isn't exposed in the TS bindings yet, or (b) it exists but under a different accessor. The code falls back gracefully (returns undefined if no metadata), and the JSON.parse is wrapped in try-catch, so there's no crash risk. However, if metadata is never actually populated on these objects at runtime, simulationContext() would always return undefined even for simulation jobs.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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.

0 participants