feat(agent): lk agent debugger, a text-mode test harness for coding agents - #977
Conversation
Rename `lk agent daemon` to `lk agent debugger` (the old name stays as a hidden alias) and rebuild it around a shared agent-session core so a coding agent such as Claude Code or Codex can test a LiveKit agent turn by turn from the shell, without a human at a microphone and without LiveKit Cloud simulations: the caller plays the user and pays only for the agent's own LLM and tool calls. New in the debugger: - `listen`: wait for the agent to speak unprompted (greetings, timers, follow-ups) and print whatever it says. - `history`, `status`, `logs [-n|-f]`, `restart`; `stop` prints a closing summary with `--transcript` and `--logs` options. - `say --timeout`, `say --logs` (interleaves the agent's log lines with the turn so a tool's traceback sits next to the sanitized error), `say` from stdin, `--verbose`, and `--json` on every command with a stable event schema (message, tool_call, handoff, config, error, log). - Tool calls render with arguments and results; handoffs and config changes are shown; silent turns say so. - Events the agent produces between turns are buffered instead of dropped, and reported with the next `say`/`listen` or at `start`. - Opening greetings print at `start`; a handoff target's introduction is held in the same turn as the handoff. Internals: `debugger_agent.go` owns the console IPC connection (read loop, response routing by request id, event normalization, undelivered buffer), `debugger_daemon.go` is the detached process and control port, `debugger.go` the CLI. Unit tests drive the core against a fake agent over an in-memory pipe; the opt-in e2e test also checks history/status/logs. Help text: `lk --help` is now sectioned (AGENTS, AGENT DEPLOYMENT, PROJECTS, ROOMS AND MEDIA, TELEPHONY, TOOLS) with agent subcommands listed inline; `lk agent --help` groups local vs cloud commands; console and debugger state their audiences; agent subcommand summaries are tidied. README gains "Agent console" and "Agent debugger" sections.
A caller that never runs `stop` (a coding agent whose task ended, a script that crashed) used to leave the agent process running indefinitely. The daemon now stops itself after --idle-timeout (default 30m) without any control command; a turn in progress counts as activity. `status` shows the idle time and limit, `restart` preserves the setting, and 0 disables it.
|
Demo of every lk-agent-debugger-demo.mp4 |
…er their step; logs --last - --verbose is gone; its two effects are separate flags: --metrics (per-reply latency and turn duration) and --full-output (untruncated tool results). - say --logs now prints a turn's log lines beneath the event they belong to, so a failing tool's traceback appears under the tool's error line rather than above it. - logs --lines/-n is now logs --last (with -n kept as the short form).
Seeing exactly what a tool returned is the point of the debugger, and --json already carried the full text, so the 600-byte cap and the flag that lifted it only got in the way.
…y → chat-history `events` prints the session as a flat, timestamped, one-line-per-event stream (messages, tool calls, handoffs, config changes, errors, agent state transitions), replaying the most recent events by default and streaming new ones with --follow; --logs adds agent log lines and --json emits NDJSON so another program can consume the stream live. It observes through a separate tap, so it never affects what `say` and `wait-for-reply` see. Events now carry a `time` field (arrival time for live events, the SDK's created_at for chat-history items), and agent state transitions are reported on the stream as `state` events. `listen` is renamed `wait-for-reply` and `history` is renamed `chat-history` (the `transcript` alias stays).
Polling chat-history (or following events) covers the need with fewer edge cases. The greeting wait at start and the between-turn buffering that say reports are unchanged.
|
Let's just make sure it is using our shared components between console and debugger, otherwise it's going to be a PITA to maintain (e.g the way we render transcripts, etc etc) |
# Conflicts: # autocomplete/fish_autocomplete
Review feedback: console and debugger each rendered the same conversation with their own code. The debugger's event schema and proto normalization now live in transcript.go, its renderer in transcript_render.go (colors from the theme palette instead of hardcoded), and the console TUI prints user turns, agent messages, tool calls, handoffs, config changes, errors, and its metrics status line through them. console_tui.go loses formatChatItem, its function-tool block, formatMetrics/formatMs, summarizeOutput/truncateOutput, and two style helpers. Transcript tests move to transcript_test.go. VRT frames are unaffected: they capture the console's status area, not the scrollback transcript.
|
@theomonnom I did some cleanup to improve code sharing, gonna merge this |
This (re)introduces a new text-mode test harness designed for coding agents to test livekit agents themselves. The goal of this change is to allow coding agents to rapidly iterate in goal loops on livekit, without overpsending cash on the new simulations feature. Simulations is reserved for integration testing, pre-production checklists, CI, etc.
This feature is built on top of the existing undocumented
lk agent daemonfeature, which is now renamedlk agent debuggerand its functionality is made more comprehensive.The change was tested extensively against livekit-examples/agent-starter-python and livekit-examples/agent-starter-node.
Follow-up work includes:
Here's the help entry for the new command:
Renames
lk agent daemontolk agent debugger(old name kept as a hidden alias) and rebuilds it into a text-mode harness that a coding agent (Claude Code, Codex, Cursor, …) or a script can use to converse with a local LiveKit agent one command at a time. The agent runs in console mode with STT/TTS off, so a turn costs only the agent's own LLM and tool calls and nothing touches a LiveKit room. It is an ad-hoc, local stand-in for simulations in which the coding agent plays the user.I've also updated the global help entry for the CLI to better capture that LiveKit is for building agents, and to unroll many of the agent subcommands for easier discovery: