Skip to content

Land Depot agents CLI (DEP-7264) - #613

Open
robstolarz wants to merge 45 commits into
mainfrom
rob/depot-agents-cli-land
Open

robstolarz wants to merge 45 commits into
mainfrom
rob/depot-agents-cli-land

Conversation

@robstolarz

@robstolarz robstolarz commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

Lands the approved Depot agents CLI stack in one PR (DEP-7264). The stacked PRs conflict pairwise, and the whole CLI can be verified locally. Each PR is a --no-ff merge onto main: #606 (607b222) → #611 (3df2ef5) → #607 (741e8d3) → #610 (4cfa7ed) → #609 (c02ffda) → #608 (44b7a93). After the merges, one fix commit validates skill names before pull --force removes anything (Bugbot finding). #612 (MCP management) is a draft and is merged in after approval.

Review: only the #609 and #608 merges have conflicts. Run git show --remerge-diff on each; resolution notes are in the details below.

Test plan (local, end to end)

  • go build -o depot-dev ./cmd/depot and go test ./... pass.
  • ./depot-dev --help has no agent. ./depot-dev agent --help lists pull, push and session, and agent session --help lists action, attach, create, interrupt, list, send and watch. session create --help shows --file, --stdin-file, --definition and --views (TestAgentIsHiddenFromRootHelp, TestAgentHelpStillWorks).
  • session create/send/watch/attach against the in-process fake server: go test ./pkg/cmd/agent -run 'TestCreate|TestSend|TestWatch|TestAttach' -v (retries with one request ID, reconnects, settles, attach forwarding).
  • Files and chat: -run 'TestUploadAttachments|TestAttach|TestChatModel|TestMediaType'.
  • pull/push: -run 'Definition|TestPull|TestPush|TestForcedPull'. This includes the new TestForcedPullWithABadSkillNameKeepsTheExistingDefinition, which fails without the fix.
  • Local MCP (attach --mcp-config) on a real stdio server: -run 'TestServe|TestStartServer|TestLoadMcpConfig'.
  • Views and session action: -run 'TestRenderView|TestRenderTodos|TestRendererPrintsViews|TestInvokeViewAction|TestWatchAdvertises'.
  • TODO(verify): session list, session interrupt and pull's RPC call have no fake-server test.
Conflict resolutions

Generated code (depot_agent.pb.go, depot_agent.connect.go): take either side, then buf generate. Re-running make generate produces no diff.

proto/depot/agent/v1/depot_agent.proto

pkg/cmd/agent/render.go: keep both imports, docker/go-units and x/term.

pkg/cmd/agent/session.go (#608)

  • session create vars: HEAD's files, stdinFile and definition, plus views. session attach vars: auth, mcpConfig and views.
  • attach Long: HEAD's text, plus Render plugin views and run their controls from the CLI (DEP-7248) #608's line about depot agent session action.
  • attach RunE: HEAD's body (local MCP startup, chat vs stdin), with views passed to chatSession and attachSession. Both flag sets are kept.
  • attachSession: keep HEAD's version, which delegates to attachLines. attachLines takes a new viewMode argument and calls r.SetViewMode(viewMode) after NewRenderer.

Compile-only fixes (no textual conflict): chatSession takes viewMode and passes it to attachLines. Four attachSession calls in files_test.go gain viewsFull.

depot agent session action stays under session, where #608 put it. Briefly split into #614–#619 and then rejoined here; those PRs are closed.

🤖 Generated with Claude Code

https://claude.ai/code/session_01NdhqP6oGqJMRJLwBU5Zepm


Note

Medium Risk
Large new client surface (sessions, uploads, subprocess MCP) against Depot APIs; mitigated by hidden GA flag, retries/idempotency keys, and terminal output sanitization.

Overview
Adds a hidden depot agent command (not shown on depot --help) wired through NewDepotAgentClient and the Depot Agent Connect RPCs.

Sessions: session create|send|watch|list|interrupt|attach|action — create sessions with repo/model/definition/attachments, stream transcripts with reconnect and --until-idle, and attach for interactive chat (Bubble Tea when TTY) with /file, /steer, /interrupt, and plugin action for view controls.

Definitions: pull / push round-trip AGENTS.md, skills/*/SKILL.md, and plugins.json, with rollback via --version and skill name validation before pull --force deletes existing files.

Attachments: SHA-256 uploads via PrepareAttachmentUploads, git-aware directory packing, limits, and stdin/--stdin-file support.

Local MCP: optional --mcp-config on attach starts stdio MCP servers (modelcontextprotocol/go-sdk), offers tools to the session, and proxies PullLocalMcpCalls / RespondLocalMcpCall.

Output: terminal-safe rendering (streaming partials, plugin views full/compact/none) and sanitized errors.

Reviewed by Cursor Bugbot for commit ca97f5b. Bugbot is set up for automated code reviews on this repo. Configure here.

robstolarz and others added 30 commits October 2, 2026 10:02
Adds `depot agent session` with create, send, interrupt, list, watch, and
attach, backed by the new DepotAgentService. The proto is copied from api,
leaving out the service only the agent's own sandbox calls.

watch renders the session's conversation view (user messages, assistant
text, tool calls and results) once per entry, reconnects with after_seq
when the stream ends, and can exit once a turn settles. attach forwards
each stdin line as a follow-up, with /steer, /interrupt, and /quit.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The harness now pushes a bounded projection as view_json instead of the raw
conversation entries, so watch and attach printed nothing. Render its
messages once by id, announce each live tool call, stream the partial
response as it grows, and note queued inputs, retries, and truncation.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The view now names every tool call with a one-line summary and gives each
tool result its call id. Print "→ name summary" once per call, from
whichever of the assistant message, the live tool slot, or the result
shows it first, so a result always follows its call.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
create now sends one client_request_id per invocation and retries
transient failures with it, so a create that landed before the error
returns that session instead of starting a second one.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- watch --until-idle and create --watch exit when a session fails or is
  archived, even if it never reported running.
- send and attach retry transient failures with the message's one
  client_request_id, as create does.
- A view that fails to decode is returned instead of retried, since only
  RPC errors can be transient.
- A running status prints before the frame's view, so it no longer cuts
  into the streamed partial and duplicates it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ted queued inputs

create --watch and --output json are now mutually exclusive.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ports an error

A partial response that cleared without a final message left its line open,
so the next response streamed onto it. Attach's command notices wrote straight
to the output and could cut into a streaming line; they now go through the
renderer, which ends that line first.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…minal

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… from the server

Format characters such as U+202E could reorder names and summaries on screen.
Errors returned by agent commands, and IDs in their success messages, now go
through the same sanitizer as the transcript. Part of DEP-7247.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The transcript prints above an input line that stays editable while output streams.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
`--file` (repeatable) on `session create` and `session send`, and `/file <path> [message]`
in attach, hash each file locally, upload only the contents Depot does not already store,
and send the attachments with the message. The size shows before upload, and a file over
the server's limit is refused before anything is uploaded.

The transcript names the sender of each user message and queued input, including people
on connected services such as Slack, and lists attached files.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… upload

Directories send the files git would track, so .gitignore applies; --stdin-file
names piped input; media types are sniffed so piped images reach the model as
images. Every path is checked against the server's count, per-file and total
limits before anything uploads. DEP-7247

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…for every hash

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…input by what the message has left

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ashed, and fit names in 255 bytes

Inside a work tree a git failure no longer falls back to a plain walk that would
send ignored files. Each file is read once, so a file that changes mid-attach
cannot upload under the wrong hash. Flattened names keep their tail and extension.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ders

A send that fails after retries leaves attach unable to send while the bad
file stays queued, so the chat drops the queue and says so. Subagent reports
render as "name (subagent)" like other non-user senders.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ent notices

Part of DEP-7247.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NdhqP6oGqJMRJLwBU5Zepm
Linux maps .log to text/x-log; macOS to text/plain. Part of DEP-7247.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NdhqP6oGqJMRJLwBU5Zepm
Matches the API's numbering. Part of DEP-7247.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NdhqP6oGqJMRJLwBU5Zepm
Watch, create and attach now ask for the "cli" view kind and print each
plugin view under a rule whenever its rev changes, sanitised and clipped
to the terminal width. `depot agent session action` runs a view's button,
select or form through InvokeViewAction, with a confirmation prompt for
confirm and danger controls.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NdhqP6oGqJMRJLwBU5Zepm
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NdhqP6oGqJMRJLwBU5Zepm
The system MIME table varies by machine and can label TypeScript as
video/mp2t, so the extension is consulted only for bytes the sniffer
cannot place. Part of DEP-7247.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NdhqP6oGqJMRJLwBU5Zepm
A cut partial no longer continues and prints whole when it finishes,
so it should not hold views back. Part of DEP-7248.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NdhqP6oGqJMRJLwBU5Zepm
…-7247)

`depot agent session attach --mcp-config <file>` starts the stdio servers in an
.mcp.json-format file and offers their tools to the session's agent over
PullLocalMcpCalls, running each call it receives and answering it. The agent sees
them only on turns answering your own messages, and only until you detach.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NdhqP6oGqJMRJLwBU5Zepm
A .docx still sniffs as a zip and an .svg or .md as text, so the
extension's type wins when it refines what the bytes say, and is
ignored when it contradicts them. Part of DEP-7247.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NdhqP6oGqJMRJLwBU5Zepm
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NdhqP6oGqJMRJLwBU5Zepm
…at start (DEP-7247)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NdhqP6oGqJMRJLwBU5Zepm
robstolarz and others added 7 commits October 3, 2026 13:24
Refs DEP-7247.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NdhqP6oGqJMRJLwBU5Zepm
Refs DEP-7264

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NdhqP6oGqJMRJLwBU5Zepm
Refs DEP-7264

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NdhqP6oGqJMRJLwBU5Zepm
Refs DEP-7264

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NdhqP6oGqJMRJLwBU5Zepm
Refs DEP-7264

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NdhqP6oGqJMRJLwBU5Zepm
Refs DEP-7264

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NdhqP6oGqJMRJLwBU5Zepm
Refs DEP-7264

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NdhqP6oGqJMRJLwBU5Zepm
@linear-code

linear-code Bot commented Oct 3, 2026

Copy link
Copy Markdown

DEP-7264

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 9998867. Configure here.

Comment thread pkg/cmd/agent/definition.go

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale comment

Risk: high. Left a non-blocking comment: Cursor Bugbot reported an unresolved medium-severity finding about pull destroying a local definition, so this is not approved. Human review is needed and no reviewers were assigned.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Router and Approver

@robstolarz

Copy link
Copy Markdown
Contributor Author

From Claude: Superseded by the per-feature landing stack, so each feature can be verified on its own: #614 (session) → #615 (hide) → #616 (files) → #617 (definitions) → #618 (local MCP) → #619 (views). This branch is kept because CI uses it as a cli_branch input; its tree is identical to #619's head.

A pull whose skill names were invalid deleted the existing definition and
then failed partway through writing, leaving a partial directory. Validate
every name before anything on disk changes.

Refs DEP-7264

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NdhqP6oGqJMRJLwBU5Zepm

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale comment

Risk: high. Left a non-blocking comment: Cursor Bugbot still has an unresolved medium-severity finding that pull can destroy a local definition, so this is not approved. Human review is needed and no reviewers were assigned.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Router and Approver

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Risk: high. Approved: Cursor Bugbot completed successfully on the current head with no unresolved findings that need human review.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Router and Approver

Comment thread pkg/cmd/agent/localmcp.go
}

// call runs one tool call. Every failure, including a server that has exited, is an error result for the agent.
func (l *localMcp) call(ctx context.Context, call *agentv1.LocalMcpToolCall) *agentv1.LocalMcpToolResult {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Agentic Security Review

Severity: MEDIUM

Description: Local MCP CallTool dispatch is not bound to the trimmed tool list this attach advertised on PullLocalMcpCalls.

Impact: A Depot-delivered CallTool can run stdio MCP tools that offerFor skipped or never listed (over-50, over-budget, empty/overlong name) while the live ClientSession stays connected. Those tools still execute in the attach user's process/environment and their results are returned on RespondLocalMcpCall. That is a real local side-effect and data-return path beyond the capability the CLI computed, notified the user about, and sent as Servers. It is not a new way to start an unconfigured server, but it does hide extra tools from the advertised allowlist on a server the user was told was only partly offered.

Remediation: In call(), resolve server_name in l.offers (or an allowlist built from the offer sent on this stream) and reject a missing/empty tool_name that is not in that server's offered Tools. Treat trimmed, over-budget, and overlong-name tools as denied even though the stdio session is still running. Optionally require a non-empty attachment_id from the matching Attached event before executing.

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.

1 participant