Repository navigation
Land Depot agents CLI (DEP-7264) - #613
robstolarz wants to merge 45 commits into
Conversation
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
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
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ 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.
There was a problem hiding this comment.
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.
Sent by Cursor Approval Agent: Pull Request Router and Approver
|
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 |
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
There was a problem hiding this comment.
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.
Sent by Cursor Approval Agent: Pull Request Router and Approver
| } | ||
|
|
||
| // 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 { |
There was a problem hiding this comment.
🔒 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.



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-ffmerge onto main: #606 (607b222) → #611 (3df2ef5) → #607 (741e8d3) → #610 (4cfa7ed) → #609 (c02ffda) → #608 (44b7a93). After the merges, one fix commit validates skill names beforepull --forceremoves 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-diffon each; resolution notes are in the details below.Test plan (local, end to end)
go build -o depot-dev ./cmd/depotandgo test ./...pass../depot-dev --helphas noagent../depot-dev agent --helplistspull,pushandsession, andagent session --helplistsaction,attach,create,interrupt,list,sendandwatch.session create --helpshows--file,--stdin-file,--definitionand--views(TestAgentIsHiddenFromRootHelp,TestAgentHelpStillWorks).session create/send/watch/attachagainst 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).-run 'TestUploadAttachments|TestAttach|TestChatModel|TestMediaType'.pull/push:-run 'Definition|TestPull|TestPush|TestForcedPull'. This includes the newTestForcedPullWithABadSkillNameKeepsTheExistingDefinition, which fails without the fix.attach --mcp-config) on a real stdio server:-run 'TestServe|TestStartServer|TestLoadMcpConfig'.session action:-run 'TestRenderView|TestRenderTodos|TestRendererPrintsViews|TestInvokeViewAction|TestWatchAdvertises'.session list,session interruptandpull's RPC call have no fake-server test.Conflict resolutions
Generated code (
depot_agent.pb.go,depot_agent.connect.go): take either side, thenbuf generate. Re-runningmake generateproduces no diff.proto/depot/agent/v1/depot_agent.protoPullLocalMcpCallsandRespondLocalMcpCall. The definition messages come before theLocalMcp*messages.DepotAgentAttachment.rpc InvokeViewActiongoes at the end of the service, afterRespondLocalMcpCall, not inside the message.DepotAgentInputkeeps bothattachments = 8(feat: send files to Depot agents and chat in attach #607) andoptional PluginCall plugin_call = 20(Render plugin views and run their controls from the CLI (DEP-7248) #608). Render plugin views and run their controls from the CLI (DEP-7248) #608 numbers its fields from 20, so nothing collides. TheInvokeViewAction*messages followRespondLocalMcpCallResponse.pkg/cmd/agent/render.go: keep both imports,docker/go-unitsandx/term.pkg/cmd/agent/session.go(#608)session createvars: HEAD'sfiles,stdinFileanddefinition, plusviews.session attachvars:auth,mcpConfigandviews.attachLong: HEAD's text, plus Render plugin views and run their controls from the CLI (DEP-7248) #608's line aboutdepot agent session action.attachRunE: HEAD's body (local MCP startup, chat vs stdin), withviewspassed tochatSessionandattachSession. Both flag sets are kept.attachSession: keep HEAD's version, which delegates toattachLines.attachLinestakes a newviewModeargument and callsr.SetViewMode(viewMode)afterNewRenderer.Compile-only fixes (no textual conflict):
chatSessiontakesviewModeand passes it toattachLines. FourattachSessioncalls infiles_test.gogainviewsFull.depot agent session actionstays undersession, 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 agentcommand (not shown ondepot --help) wired throughNewDepotAgentClientand 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/pushround-tripAGENTS.md,skills/*/SKILL.md, andplugins.json, with rollback via--versionand skill name validation beforepull --forcedeletes existing files.Attachments: SHA-256 uploads via
PrepareAttachmentUploads, git-aware directory packing, limits, and stdin/--stdin-filesupport.Local MCP: optional
--mcp-configon attach starts stdio MCP servers (modelcontextprotocol/go-sdk), offers tools to the session, and proxiesPullLocalMcpCalls/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.