Repository navigation
feat(subagents): stream normalized turn events - #410
Merged
Merged
Conversation
…rovider instance name A profile named like its provider instance won target resolution on start but was treated as a raw provider run on continue, dropping its instructions and its disabled check. Resolve the profile first on continue too. Also make the migrated local_agent_sessions.driver column NOT NULL so migrated databases enforce the same invariant as fresh ones. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A cached probe could keep routing new runtimes to the previous protocol generation after OpenCode was upgraded while the daemon stayed up. Runtime creation already spawns a server, so one extra --version call is negligible. Also report spawn failures other than ENOENT with their cause. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… message OpenCode 2 records a turn that fails before any assistant output (for example an expired provider login) as a failed idle message. The assistant-only listing hid it and the run ended with a misleading protocol error. Read the failed idle message and surface its error. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Abort the local OpenCode request before awaiting the provider's abort so a stalled provider call cannot keep the turn pending. Do not submit a prompt to Pi, Claude, Codex, or ACP once stop was requested during setup. Give ACP providers a grace period to answer session/cancel before dropping the process. A failed Codex interrupt no longer reports the turn as stopped. The manager bounds how long stop waits (below the client deadline) and reports a conflict instead of success when the turn kept running or finished normally. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Drain Pi's stderr and report its tail when the process dies, handle stdin errors on the connection instead of letting EPIPE crash the daemon, and drop events left over from an earlier turn. A stopped or timed-out turn aborts Pi and discards a process that does not settle, and a stop requested before the prompt never reaches Pi. Resolving a model now prefers an exact model id over provider/model parsing, and resuming a bare session id from before the RPC runtime fails explicitly instead of silently opening an empty session. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…h startup A named instance using a legacy ACP driver (driver: "cursor") lost its flavor and started without the provider's required arguments, while a built-in id paired with another driver was wrongly given ACP flavor config. Availability now resolves the ACP executable through the same function the driver uses, and configured built-in ACP ids are no longer listed twice or dropped. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The RPC runtime waits for agent_settled, which the previously pinned 0.80.3 does not emit, so turns on the bundled CLI would only end at the timeout. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Preflight only looked for the bundled package, rejecting a system pi that the runtime would happily launch. Also update stale Pi docs for the RPC runtime. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Each run now gets a trn_ id from the manager, persisted with the turn history. Drivers report activity through onEvent; the manager owns turn_started/turn_completed and stores a bounded, redacted log per turn. Failed turns also keep the provider's own error detail. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds agent.events to the daemon protocol (v8) and `devspace agents events <id> [--turn <id>] [--follow]`, which polls for events after the last seen seq. show, ls, run and wait now carry the turn id, and failed turns print the provider's cause. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude maps complete SDK assistant/tool_result messages; Pi maps its tool_execution and message_end RPC frames. Both report turns.streaming, and OpenCode reports false. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex maps app-server item/started and item/completed for commands, file changes, MCP tools and web search. ACP maps session/update tool_call frames and joins agent_message_chunk deltas into messages. Codex turn failures now keep the provider's error message as the cause. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Owner
Author
|
Re the outside-diff finding ( |
…stance Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e-v1-v2 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…stry Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…apabilities-cancel Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…c-acp Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A continued v2 turn that fails before output no longer returns the previous turn's assistant message, and the version probe reads output after stdio closes rather than on exit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ubagent-capabilities-cancel
A stop that outlives the cancel grace period closes the runtime, which must not end other agents' turns sharing the same workspace. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The local request now ends only once the native abort (v1) or interrupt (v2) succeeds. A failed or stalled one keeps the turn running, so the manager reports a stop conflict instead of a false stopped state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…or/subagent-driver-registry
…y' into feat/subagent-capabilities-cancel
…l' into refactor/generic-acp # Conflicts: # src/local-agent-acp.test.ts # src/local-agent-acp.ts
Cursor, Copilot and Grok derive their sandbox flags from the write mode; replacing their arguments would drop those restrictions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DevSpace cannot bound what an arbitrary ACP agent writes, since it may act without a permission request. Restricted write modes are refused instead of being silently unenforced. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Passwords may contain '@', and a query string may contain '@' after a port; redact up to the authority's last '@' and never past '/', '?' or '#'. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
alexph-dev
pushed a commit
to alexph-dev/devspace
that referenced
this pull request
Oct 26, 2026
feat(subagents): stream normalized turn events
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.
Until now a running subagent was a black box.
LocalAgentRuntime.run()only returned when the turn ended, the host had no way to see what the worker was doing, and nothing identified an individual turn, so there was nothing to tie a subagent's work to later review changes. On top of that,agents showhid why a turn failed: hosts sawClaude agent turn failed.while the provider's real error ("the selected model does not exist", "usage limit reached") stayed inside the error cause.This PR gives every turn an app-owned
trn_id and a small, normalized event stream defined in DevSpace's own terms. The types areturn_started,message,tool_started,tool_completed(tool, short summary, status) andturn_completed(completed/failed/stopped). The manager allocates the turn id and emits the two lifecycle events itself. Each driver maps its native frames into activity through a newonEventcallback, so no provider frame enters the domain model. Before persisting, the manager redacts and bounds each event. Tool summaries keep only the identifying argument (a command, path, or query), never full inputs or outputs. A turn keeps at most 500 events and 256 KiB, with the last slot reserved forturn_completed. Only an agent's 20 most recent turns keep their event logs. The log is exposed through a newagent.eventsdaemon method (protocol v8) anddevspace agents events <id> [--turn <id>] [--follow], written as the existing XML fragments. The daemon protocol is request/response, so--followpolls every 500 ms for events after the lastseqit has seen. It also pins the turn it started on, so it stays on that turn even if the agent is continued.show,ls,run, andwaitnow includeturn="trn_…". Failed turns also persist the provider's own message, redacted and capped at 1,000 characters, shown as<cause>in XML anderror.detailin JSON.Streaming support is reported by
capabilities.turns.streaming. It sits next tointerruptto match the existing nested shape. Claude maps complete SDKassistant/tool_resultmessages. Pi mapstool_execution_*andmessage_endRPC frames. Codex maps app-serveritem/started/item/completedfor commands, file changes, MCP tools, and web search. ACP mapstool_call/tool_call_updateand joinsagent_message_chunkdeltas into one message per stretch of text. OpenCode v1 and v2 reportstreaming: false: both run the turn as one blocking prompt call, and their SSE event stream would be its own change. They still getturn_started/turn_completedfrom the manager. Claude and Pi were verified live. Codex and OpenCode were not, because both accounts are out of quota. Codex and ACP are covered by fake-transport tests: a fakecodex app-serverprocess and the ACP notification path.I did not change the
open_workspacecard. The MCP server has no connection to the agent daemon today, since subagents run only through the CLI and daemon. The card is also a one-time snapshot ofopen_workspacewith no refresh path. Showing live subagents there would need two things: the server opening a read-only client to the daemon (or the store) duringopen_workspace, and a way for the card to refresh, which today means a new app-callable tool. That is a product decision, not a drive-by. Once both this PR and #407 land,WorkspaceActivityJournal.currentTurn(workspaceId)can be backed directly byagent.events. It would list the workspace's agents, take each one'slatestTurnId, and page events byafterSeq. Thetrn_id is the stable key a review checkpoint can record to link changes back to the turn that made them.Live smoke used the built CLI (
node dist/cli.js) with a tempDEVSPACE_CONFIG_DIR, a throwaway git repo, and Pi 1.4.0 with an isolatedPI_CODING_AGENT_DIR(xai/grok-4.5). The state dir had to live under a short/tmppath, because a long scratchpad path exceeds the Unix-socket length limit; that limit predates this PR. Trimmed transcripts are below. The Claude turn shows its sandbox rejecting a read and edit outside the workspace (status="failed"). The Pi turn shows parallel tool calls being correlated. A Pi turn stopped duringsleep 30ended the follower withturn_completed status="stopped" code="PROVIDER_CANCELLED"; Pi sends notool_execution_endfor the cancelled tool, so thattool_startedstays unpaired. A Claude turn with a bogus model showed the new cause:agents events --followduring a real Claude turn (events 9–12 elided)agents events --followduring a real Pi turnMigration 11 adds
turn_idanderror_detailtolocal_agent_turns,latest_turn_idanderror_detailtolocal_agent_sessions, and alocal_agent_turn_eventstable, backfillingtrn_<rowid>ids for existing turns. The migration runner rejects unknown versions, so any other open PR that claims version 11 will need renumbering when the two meet.🤖 Generated with Claude Code
Summary by CodeRabbit
devspace agents events <id>to view an agent turn’s activity, with--followto watch until it ends and--turnto select an earlier turn.