Skip to content

feat(cli): discover AgentServer in standalone CLI#1735

Open
rosetta-livekit-bot[bot] wants to merge 1 commit into
mainfrom
earring-muzzling-monkey
Open

feat(cli): discover AgentServer in standalone CLI#1735
rosetta-livekit-bot[bot] wants to merge 1 commit into
mainfrom
earring-muzzling-monkey

Conversation

@rosetta-livekit-bot

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

Copy link
Copy Markdown
Contributor

Summary

  • add standalone livekit-agents start entrypoint discovery for exported AgentServer instances
  • prefer exported app, server, then agent, and fall back to a single exported AgentServer
  • allow standalone CLI flags/env to update connection options before running the discovered server
  • add the console command surface with an explicit unsupported error because agents-js does not yet have the Python TCP console runtime/fake-job integration

Verification

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

Notes

  • No tests added, matching the source change.

Ported from livekit/agents#6024

Original PR description

No description.

@changeset-bot

changeset-bot Bot commented Jun 9, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 9cb8aec

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 04:23

@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 3 additional findings in Devin Review.

Open in Devin Review

Comment on lines +147 to +149
).action(async (entrypoint: string | undefined, command: Command) => {
const opts = { ...program.opts(), ...command.opts() } as RunOptions;
await runDiscoveredServer({ entrypoint, production: true, opts });

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.

🔴 Commander action handler receives options object instead of Command instance due to positional argument

In Commander v12, action handlers for commands with positional arguments receive parameters in the order (arg1, options, command). The start [entrypoint] command has one positional arg, so the handler receives 3 arguments: (entrypoint, optionsObject, commandInstance). However, the handler only declares two parameters (entrypoint, command), which means command actually receives the plain options object {logLevel: 'info'} (the 2nd arg), not the Command instance (the 3rd arg). When command.opts() is subsequently called, it throws TypeError: command.opts is not a function because plain objects don't have an .opts() method. This contrasts with the existing cli.ts which correctly handles commands WITHOUT positional arguments using (...[, command]) to skip the options object and receive the Command instance.

Suggested change
).action(async (entrypoint: string | undefined, command: Command) => {
const opts = { ...program.opts(), ...command.opts() } as RunOptions;
await runDiscoveredServer({ entrypoint, production: true, opts });
).action(async (entrypoint: string | undefined, _options: unknown, command: Command) => {
const opts = { ...program.opts(), ...command.opts() } as RunOptions;
await runDiscoveredServer({ entrypoint, production: true, opts });
Open in Devin Review

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

Comment thread agents/src/worker.ts
Comment on lines +390 to +394
this.#opts.wsURL = opts.wsURL || this.#opts.wsURL;
this.#opts.apiKey = opts.apiKey || this.#opts.apiKey;
this.#opts.apiSecret = opts.apiSecret || this.#opts.apiSecret;
this.#opts.workerToken = opts.workerToken || this.#opts.workerToken;
this.#opts.logLevel = opts.logLevel || this.#opts.logLevel;

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.

🟡 updateOptions skips workerToken Cloud deployment constraints enforced by constructor

The AgentServer constructor (agents/src/worker.ts:309-323) enforces Cloud deployment constraints when workerToken is set: it resets loadFunc to defaultCpuLoad and loadThreshold to the production default. However, updateOptions at line 393 simply assigns opts.workerToken without replicating these constraints. If a user creates an AgentServer without a workerToken (allowing custom loadFunc/loadThreshold) and the CLI later provides --worker-token via updateOptions, the server will run in Cloud mode with potentially incompatible custom load settings, bypassing the safety checks the constructor was designed to enforce.

Prompt for agents
In agents/src/worker.ts, the updateOptions method at line 390-394 sets workerToken without applying the same Cloud deployment constraints that the constructor applies at lines 309-323. When workerToken is newly set via updateOptions, the method should also check and reset loadFunc to defaultCpuLoad and loadThreshold to Default.loadThreshold(this.#opts.production) with appropriate log warnings, mirroring the constructor logic. This ensures that Cloud deployment constraints are always enforced regardless of whether the workerToken was provided at construction time or later via updateOptions.
Open in Devin Review

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

Comment on lines +99 to +107
const server = await discoverAgentServer(entrypoint);
server.updateOptions({
apiKey: opts.apiKey,
apiSecret: opts.apiSecret,
logLevel: opts.logLevel,
workerToken: opts.workerToken,
wsURL: opts.url,
});
await runAgentServer({ logLevel: opts.logLevel, production, server, watch: false });

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.

🚩 Production mode mismatch between CLI and discovered AgentServer

The start command in livekit-agents.ts:149 always passes production: true to runAgentServer. This affects SIGINT/SIGTERM drain behavior and log formatting. However, the discovered AgentServer instance was constructed by the user's module with its own production flag (which determines loadThreshold, numIdleProcesses, and port defaults at agents/src/worker.ts:222-246). The updateOptions method doesn't include production in its allowed fields. This means if a user constructs their AgentServer with production: false defaults and then runs livekit-agents start, the server will have dev-mode pool settings (0 idle processes, infinite load threshold) while the CLI assumes production behavior (drain on SIGINT). This might be intentional (letting users fully configure their exported server), but it could surprise users who expect start to imply full production defaults.

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