simulate: Node agent support, --view mode, and TUI/start UX fixes (#878)
* feat(session): support Node.js agents in the local session daemon
The `lk agent session` daemon could previously only spawn Python agents;
Node/JS projects were rejected at detection time. This wires up the Node
path so a JS/TS agent can be driven over the same text-mode console
protocol as Python.
- agent_utils.go: stop gating `detectProject` on Python-only. Accept Node
projects too (DetectProjectRoot already recognizes them via package.json)
and update the error to reflect both supported runtimes.
- simulate_subprocess.go: extract interpreter/argv resolution into
`buildAgentCommand`, branching on project type. Python keeps
`<python> <entry> <args>` (uv prefixes `run python`); Node runs
`node [--experimental-strip-types] <entry> <args>`. Add `findNodeBinary`
(resolves `node` from PATH) and `isTypeScriptEntry` so a `.ts`/`.mts`/
`.cts` entrypoint runs directly via the type-stripping loader with no
build step.
- session_daemon.go: drop the stale TODO; the daemon now spawns either
runtime via `buildConsoleArgs` (`console --connect-addr <addr>`).
Verified end-to-end: `lk agent session start examples/src/console_text_agent.ts`
spawns the JS agent, connects to the Go TCP console server, completes the
text-mode handshake, and round-trips multi-turn `say` requests including a
function-tool call.
Co-authored-by: Cursor <cursoragent@cursor.com>
* feat(agent): Node.js agent support for lk agent dev/start/console (#868)
* feat(agent): support Node.js agents in lk agent dev/start/console
Builds on the Node project detection and spawn helpers from the session
daemon work (#861) to wire the remaining agent run commands for Node:
- `lk agent dev`/`start`: spawn the agents-js `dev` subcommand (Python
keeps `start --dev`), normalize --log-level casing per runtime
(agents-js only accepts lowercase, Python expects uppercase), and make
the reload-server handshake (--reload-addr) Python-only — Node reloads
are a plain kill+respawn since agents-js has no job-restore protocol.
- Entrypoint discovery: probe per-runtime candidate lists (agent.ts,
agent.js, src/agent.{ts,js} for Node; agent.py, src/agent.py for
Python) instead of a single root default, and make the not-found
message runtime-aware.
- Probe `node --version` before running a TypeScript entrypoint and fail
with a clear message on Node < 22.6 (no --experimental-strip-types).
- resolveCredentials: ignore the global --url flag's default value so
the project config (--project) can supply the URL; previously the
spawned agent was always pointed at http://localhost:7880 unless --url
or LIVEKIT_URL was set explicitly.
`lk agent console` needed no changes beyond #861 — the spawn path was
already runtime-agnostic.
Tested end to end against agents-js' console CLI branch (#1706):
console text mode, audio mode (mic/speaker + playback-finished
handshake), ctrl-T mode toggle, --record output, dev worker
registration, file-watch reload, and clean shutdown.
* feat(agent): load discovered .env files into Node agents via --env-file
Node agents spawned by lk agent dev/start/console inherited only the shell
environment, so credentials in a project .env file were never seen (Python
agents conventionally load_dotenv() themselves). Discover a known env file
in the project dir and pass it to Node's built-in dotenv with --env-file.
The flag requires Node >= 20.6, so the type-stripping version check is
refactored into a shared nodeVersion probe and the flag is skipped on older
Nodes. Shell-exported variables still take precedence over file values.
* feat(console): fail fast when the agent job crashes before connecting
A pre-connect job crash (e.g. missing API key in the entrypoint) leaves the
worker process alive, so lk agent console sat on the spinner for the full
60s connect timeout before surfacing the traceback. Scan agent output for
crash markers (Python's "job crashed" shutdown reason, agents-js's FATAL
"console mode failed:") via a new FailSignals option on AgentStartConfig,
and abort the connect wait immediately with the recent logs. The session
daemon's wait gets the same treatment, reporting the log path through the
ready pipe.
Verified against agents @ a61578853: a missing .env now fails in ~4s with
the full traceback instead of timing out at 60s.
* Revert "feat(agent): load discovered .env files into Node agents via --env-file"
This reverts commit 2430a241a34354bc33a40b4c307bbb91961f1eb3.
Per PR review, agent code is expected to load its own env; auto-loading
a discovered .env file second-guesses that. Arbitrary arg forwarding
(e.g. a -- separator) is the preferred ease-of-use mechanism instead.
* feat(agent): forward runtime args after -- to node/python
Per PR review, replace the reverted .env auto-loading with explicit
arg forwarding: everything after a -- separator is passed to the
runtime interpreter ahead of the entrypoint in lk agent
dev/start/console and the session daemon, so
`lk agent console agent.ts -- --env-file=.env` runs
`node --env-file=.env agent.ts console ...`.
urfave/cli strips the -- and folds the trailing args into the
positionals, so splitForwardedArgs recovers the split from os.Args;
detectProject uses it so a forwarded flag with no entrypoint argument
isn't mistaken for one. The detached session daemon receives the args
JSON-encoded via LK_SESSION_FWD.
* test(agent): trim agent run tests to the fail-signal case
Remove the log-level, entrypoint-resolution, arg-splitting, and command-
building unit tests along with the session forwarded-args round-trip test,
keeping TestAgentProcessFailSignal.
* simulate: Node agent support, --view mode, and TUI/start UX fixes
Ports our simulate work onto the Node-support branch:
- Node agent start: normalizeLogLevel, --log-format only for Python,
main.ts/main.js entrypoints, and refuse entrypoint discovery when
package.json defines a build task; drop the Python-only guard.
- `lk agent simulate --view <runID>`: open a pre-existing simulation and
print the reopen command on close.
- TUI: ←/→ arrow navigation in/out of simulation detail, richer
agent-exit errors (start command + recent output), and quit when the
agent fails to register.
* fix(agent): resolve Node SDK version via the runtime, gate Node separately
The local-run version gate ran main's file-based CheckSDKVersion for Node
too, which (a) couldn't find @livekit/agents in monorepo/workspace layouts
where the dep is a `workspace:*` symlink rather than a versioned entry, and
(b) gated on the Python thin-CLI baseline (1.6.0), the wrong floor for the
Node SDK.
Resolve the installed @livekit/agents via Node's own module resolution
(embedded node_resolve_version.js), starting from the entrypoint's directory
so pnpm/workspace symlinks and hoisting resolve exactly as they will at
runtime, and gate Node on its own nodeAgentMinVersion. Python keeps the
existing CheckSDKVersion path. Export IsVersionSatisfied for the reused
comparison.
---------
Co-authored-by: Toubat <brian.yin@livekit.io>
Co-authored-by: Cursor <cursoragent@cursor.com> U
u9g committed
d92b2d7ffe68ded92a6693d12aa3e444a5fac6bb
Parent: e88443e
Committed by GitHub <noreply@github.com>
on 6/25/2026, 2:21:11 AM