agent_ui: Add CLI support for sending agent prompts - #62060
Open
jutaz wants to merge 1 commit into
Open
Conversation
|
We require contributors to sign our Contributor License Agreement, and we don't have @jutaz on file. You can sign our CLA at https://zed.dev/cla. Once you've signed, post a comment here that says '@cla-bot check'. |
Author
|
@cla-bot check |
|
The cla-bot has been summoned, and re-checked this pull request! |
jutaz
force-pushed
the
agent-cli
branch
6 times, most recently
from
August 2, 2026 07:00
37c94c1 to
5cc306d
Compare
Adds `zed --agent` so an agent turn can be started in a running Zed instance from a script, git hook, or webhook. Prompts are routed through the same paths the agent panel uses when you press Enter: an idle thread sends immediately, and a busy one queues the message so it drains when the current turn stops. Threads are addressed by a durable id (in full or as a unique fragment), by ACP session id, or implicitly as the most recent thread for the working directory. The request travels over the existing `zed-cli://` IPC channel rather than the OS-registered `zed://` scheme, which deliberately refuses to auto-submit because any web page can reach it. Prompts are still run through the same sanitizer, since webhook payloads are untrusted input.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Objective
Zed's agent is driven from inside the app. Making it reachable from the command
line opens up a category of "kick off the agent when X happens" workflows —
scripts, git hooks, CI, webhooks.
There's an existing
zed://agent?prompt=…URL, but it's built for a differentpurpose:
zed://is an OS-registered scheme (osx_url_schemes = ["zed"],x-scheme-handler/zed), so any web page can navigate to it. It pre-fills themessage editor and shows a "Review Before Sending" callout, deliberately leaving
the final keystroke to a human. That's the right behavior for an untrusted
caller.
Requested in #58262, which asks for exactly these two capabilities — enumerating
threads and sending a user message to an existing one — for automation and
orchestration workflows. Adjacent asks: #54221 (scheduled prompts, which needs a
way to deliver a prompt later) and #49313 (controlling agent runs remotely).
Distinct from #59146, which proposes running the agent as a standalone headless
binary; this drives the agent already running in your editor.
Solution
Add a
--agentflag group to thezedCLI. It travels over the existingzed-cli://IPC channel, which is not OS-registered and is only reachable from alocal process, so it can submit directly. The
zed://agentpath is not touchedby this change.
Highlights
agent_ui::agent_climodule resolves a thread and dispatches a prompt intoit. Threads are addressed by
ThreadId(in full or as a unique fragment, withhyphens optional), by ACP session id, or implicitly as the most recently
updated thread for the working directory.
ThreadView::send_contentwhen the thread is idle,ThreadView::add_to_queuewhen it's generating. A queued message shows up as a real queue entry that you
can steer or delete, and it drains on
AcpThreadEvent::Stopped.AgentPanelis per-Workspacerather than per-window, so this iterates
MultiWorkspace::workspaces()insteadof
MultiWorkspace::panel(), which only sees the active workspace. A threadthat's open in a background project is reachable without stealing focus or
reloading it from disk.
--agent-listprintsid age title (open), and--agent-list-format jsonemits an array withid,title,updated_at,interacted_at,is_open, and the thread's worktreepaths. The array shapeholds even when nothing matches, so it pipes straight into
jq. The promptpaths keep stdout to the bare thread id so
id=$(zed --agent …)works.CliRequest::Agentvariant, appended afterSetOpenBehaviorso theexisting
OpenandSetOpenBehaviorvariants keep their wire representation.ThreadIdgainsDisplay/FromStr. Prompts run through the existingExternalSourcePromptsanitizer, which strips bidi and control characters —worth keeping for a path that's often fed webhook payloads.
--agent-profileand--agent-modelconfigure a thread at creation andrequire
--agent-new. A thread persists both, and changing the profile alsoswaps the model and cascades into any running subagents, so applying them to an
existing thread would silently reconfigure something the user already owns.
Both are validated before anything is dispatched — an unknown profile lists the
configured ones, an unconfigured model is an error — so a typo can't leave a
thread running under the wrong tool set.
tools, and
--agent-waitblocks until you answer, matching what happens whenyou send the same prompt by hand.
docs/src/reference/cli.mddocuments the new flags alongside the existingones.
Testing
Verified manually against a debug bundle (
script/bundle-mac -d -i), driving arunning instance over the real IPC path:
--agent-newcreates a thread and prints its id;--agent-listthen shows itmarked
(open).--agent-threadresolves a full id, an 8-character prefix, and a hyphenlessfragment to the same thread.
agent is busy; message queuedon stderr with the bare id on stdout, and the message appeared in the panel as
a queue entry.
--agent-listshowedonly its own threads.
--agent-projectreached across to the other project,and a mismatched scope failed with exit code 1.
--agent-list --agent-list-format jsonreported the linked worktree's path, and resolving a branch to its worktree
then piping the id into
--agent-threaddelivered a message to the rightthread.
--agent-list-formatis rejected outside--agent-list, and anunmatched project still yields
[].echo … | zed --agent -reads the prompt from stdin.--agent-waitblocked for the duration of the turn, then exited 0.--agent-profileand--agent-modelare rejected without--agent-new, andwith it a bad profile reports
unknown agent profile "nope"; available profiles: ask, coding-agent, ..., writewhile--agent-model bogus/modeland--agent-model notaslasheach fail with their own message. A valid profilecreates the thread, and sending to an existing thread still works.
--agent-projectwithout--agent,--agenttogether with
--agent-list, and file-opening flags (--diff,--wait,--new, …) in agent mode.A user's unsent draft is left alone: the CLI calls
send_content, which unlikesend_implnever touches the message editor. There's a test pinning this, and Iconfirmed it fails if the editor is cleared.
Automated coverage: 21 unit tests in
agent_cli(id parsing and normalization,prefix ambiguity, most-recent tie-breaking, path matching, prompt sanitization),
28 in
cli(argument validation), and 4 inopen_listener(list formatting).The dispatch functions themselves are covered by the manual runs above rather
than by tests.
./script/clippyandcargo fmt --checkare clean.Reviewers can try this on macOS with
script/bundle-mac -d -i, then use the CLIinside the bundle at
/Applications/Zed Dev.app/Contents/MacOS/cliso requestsattach to the running instance.
Self-Review Checklist:
Release Notes:
zed --agentfor starting an agent turn from the command line