Skip to content

Subagent permission/error events leave external integrations no way to see root-session progress (pane misreported as blocked/needs-input while subagent runs) #46685

Closed as not planned

Description

@anyingiit

Description

Child-session events (permission.asked, question.asked, session.error) are broadcast on the global event bus with only the child's sessionID and parentID in the payload, while the parent session's own status keeps reporting busy (or goes silent) for the whole duration of a subagent run. External consumers therefore cannot distinguish "a subagent inside this root session hit a transient stream error / a rule-evaluated permission that resolves itself" from "this root session is actually waiting on the user", and there is no reliable signal on the root session telling them the subagent recovered or finished.

Concretely: the herdr integration plugin (installed via herdr integration install opencode) maps child-session permission.asked / question.asked / session.error to blocked for the pane. When a subagent starts and later hits a transient stream error (which opencode auto-retries), the pane goes red "blocked" and plays the notification sound, even though:

  • the screen shows no permission/error UI at all (the subagent is streaming and editing files normally), and
  • the root session status never reflected anything but busy.

Observed on a real run: while a code-writer subagent ran for ~15 minutes, the pane stayed blocked the whole time with zero state changes, while opencode's log showed continuous subagent streaming (mode=subagent) and file edits; herdr agent explain --json reported visible_blocker: false. The same session's log later showed AI_APICallError: Cannot connect to API ... socket connection was closed unexpectedly on the subagent session, which matches the plugin's blocked trigger.

A controlled capture of the SSE event bus (GET /event) while a subagent ran shows the gap directly:

session.status  ROOT {type: busy}          <- last root event before the subagent starts
session.created SUB ses_... (parentID set)
session.status  SUB  {type: busy}  xN
session.status  SUB  {type: idle}
session.idle    SUB
session.status  ROOT {type: busy}          <- root only updates again after the subagent finished

No permission.* events were emitted for an auto-allowed call, and no root-session event marks "a subagent of mine started / is running / failed transiently / recovered".

Request: surface subagent lifecycle on the root session (e.g. root-visible session.status reflecting child progress, or task.* / child-session state events that integrations can use), so external integrations can avoid rendering a running subagent (or its auto-recovering stream errors) as "waiting for user input".

Plugins

herdr-agent-state.js (installed by herdr integration install opencode, v10) — its behavior is the symptom, but the underlying issue is on the opencode event model side: child-session session.error reaches plugins with no root-session context and no follow-up "recovered" event on the root session.

OpenCode version

1.18.25

Steps to reproduce

  1. Install the herdr integration for opencode (herdr integration install opencode) and run opencode inside herdr (any opencode ≥1.18 with plugins).
  2. Prompt the agent to dispatch a subagent that runs a few minutes (e.g. a code-writer subagent editing several files), or let a subagent hit a transient API stream error (it auto-retries).
  3. While the subagent runs normally (or during its automatic retry), the herdr pane shows red blocked and plays the notification sound; no approval or error UI is visible in the TUI. herdr agent explain <pane> --json shows state blocked with visible_blocker: false.
  4. Equivalent without herdr: capture GET /event on the opencode server while dispatching a subagent (see event sequence above) — the root session emits nothing between subagent start and completion, while child session.status/session.error events appear with only the child sessionID.

Screenshot and/or share link

Not applicable (state observed via herdr agent list / herdr agent explain; can provide on request).

Operating System

macOS 26.6 arm64

Terminal

Ghostty 1.3.1

Activity

  1. github-actions commented on Sep 1, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

  2. anyingiit commented on Sep 1, 2026

    @anyingiit
    Author

    Thanks for the pointers — I checked all three. None is an exact duplicate of this report, but there is real overlap with #45549 on one point.

    One correction to my own body text: I wrote that child events carry "only the child's sessionID and parentID" — that's true for session.created only; session.status carries neither, which matches #30043.

    If maintainers prefer consolidating, the "surface subagent lifecycle on the root session" ask fits as a consumer-impact case under #45549 (point 3) / #30043 — happy to move the repro there.

  3. Samir-Guenchi commented on Sep 28, 2026

    @Samir-Guenchi

    The current gap is that delegated child sessions emit session.status with only their own session ID and status. An external consumer can therefore mistake a child becoming idle or retrying for a root-session transition unless it separately correlates session.created events or fetches Session.Info.

    A narrow fix would add an optional parentID to the existing session.status event, populated centrally from the existing session record. The current busy/retry/idle values would remain unchanged; this would only make the event self-describing and would not introduce a new lifecycle system.

    This is a concrete case in #46685, while #30043 is the historical proposal for the same additive field. The scope here is limited to child-to-parent attribution: it does not attempt ACP permission routing, CLI NDJSON child-event forwarding, UI behavior, or root-level progress aggregation.

    Would maintainers prefer this narrow session.status.parentID change as the next step for #46685, or is there a preferred event/lifecycle contract?

  4. remorses commented on Sep 30, 2026

    @remorses
    Contributor

    Related V2 case from Kimaki, a Discord client (OpenCode 2.0.19): adding rootSessionID to child-session events would simplify routing after a reconnect. Right now I walk session.get → parentID until I find a bound root, buffering child events during those lookups.

    An explicit subagent-link event would also help: { parentSessionID, childSessionID, assistantMessageID, toolCallID, agent, description, background }, including when an existing child is reused. Today I retain the parent subagent call arguments until a later tool.progress.metadata.sessionID links the call to its child.

    Those two additions would remove ancestry lookups and the pending-call correlation map. This is a V2 API suggestion; the original report here is V1.

  5. thdxr commented on Oct 11, 2026

    @thdxr
    Member

    Thanks for the report! OpenCode 2.0 is now out and development is focused on V2, so we're closing issues filed against OpenCode 1. Please upgrade (opencode upgrade --major, details: https://anoma.ly/notes/opencode-2-0), and if this is still relevant on 2.0, reopen this issue or open a new one with your 2.0 version.

    — from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions