Skip to content

[FEATURE]: agents: expose child-session observability and recovery tools (status/messages/context, fork+prompt, cancel) #59566

Open

Description

@ThanatoS-cdr

Feature hasn't been suggested before.

  • I have verified this feature I'm about to request hasn't been suggested before.

Describe the enhancement you want to request

Orchestrating sessions can spawn subagents but have no agent-facing tools to observe or recover child sessions: no status, no message/context read, no salvage, no cancel. The server HTTP API already exposes everything needed; the request is to surface a scoped subset as agent tools.

The gap, from a real incident (opencode 2.1.0, 2026-10-26):

  1. A background subagent completed a 40-item legal/linguistic analysis and was killed by a provider stream cut during its final artifact write (see companion bug report). The child session ended in a "completed"-looking state with the full analysis in its context and 0 bytes of artifact on disk.
  2. The orchestrating session tried to rescue it and had no tool for any of it: it could not check whether the child was running/idle/stuck, could not read the child's messages or context, could not cancel the stuck session, and the sessionID continuation hung (companion bug).
  3. The entire diagnosis had to be driven through raw opencode api calls from the shell and log grepping; cancellation was only possible via the UI close button.

Expected behavior: parent agents should have a minimal, read-mostly toolset for children they spawned, mirroring existing server capabilities.

Current state: the agent-facing toolset exposes only session_move/session_rename/worktree tools — nothing for child-session observability or recovery. Meanwhile, the V2 HTTP API already provides: session.get, session.inbox.list/cancel/update, session.message.list/get, session.context (active model-context projection), session.fork (copies full message history; forks adopt instruction state), session.interrupt, session.remove, and experimental session.wait/session.export/session.import, plus write-ahead execution claims with startup recovery.

Proposed tools (scoped to children spawned by the calling session):

  1. session.status(childID) → { state: idle|running|claimed|unknown, pendingInbox, lastActivity } — distinguishes a working child from a zombie without log grepping.
  2. session.messages(childID, limit) / session.context(childID) — read-only harvest of a child's message history / model-context projection (the rescue path for finished-but-unwritten work).
  3. subagent.fork(sessionID, prompt) — fork with full history + prompt in one call: salvage a hung child's completed analysis without redoing the work.
  4. subagent.cancel(sessionID) — programmatic parity with the UI close (map to session.interrupt / inbox.cancel / session.remove).

Concrete use case (real): a child session holds a completed 40-item audit analysis; the final write dies on a stream cut. With the proposed tools the parent would do: session.status → confirm stuck → session.messages harvest → subagent.fork(child, "write the artifact now, in ≤10 KB segments; if your context lacks the complete results, say so instead of fabricating") → artifact salvaged. Today the only options are losing the work (fresh relaunch) or manual opencode api/UI surgery from outside the agent loop.

These tools also serve as the missing supervision layer for the companion bug: status + pendingInbox makes a silently-dead child visible, and cancel clears it without desktop UI access.

Related issues:

Activity

  1. opencode-agent commented on Oct 27, 2026

    @opencode-agent
    Contributor

    Thanks for the detailed write-up, @ThanatoS-cdr! You're asking for a small set of agent tools that let an orchestrating session inspect and recover the child sessions it spawned: status, reading messages/context, fork + prompt, and cancel. These would expose routes the server already has. Maintainers will consider it.

    Could this be a plugin today? Only partly. The v2 plugin session API already has get, context, interrupt, remove and wait, so status, context read and cancel could be added as plugin tools. It doesn't have message listing, fork or the inbox, so the message-harvest and fork + prompt parts can't be built as a plugin yet.

    Related issues:

  2. ThanatoS-cdr commented on Oct 27, 2026

    @ThanatoS-cdr
    Author

    Thanks for the triage and the plugin-path analysis — that split matches our use case exactly. In the incident behind the companion bug (#59565), the pieces that actually recovered the work were the message harvest + fork + prompt, so those are the ones that can't stay plugin-less long-term. The supervision subset (status / context read / cancel) looks like a plugin-sized first step — happy to prototype it and report back how far the existing plugin session API gets.

  3. ThanatoS-cdr commented on Oct 27, 2026

    @ThanatoS-cdr
    Author

    Follow-up as promised — prototype done and tested headless on 2.1.0.

    What was built: a small plugin ("subagent-supervisor" — plain ESM JS, no dependencies) registering three direct agent tools on the current plugin session API: session_status(sessionID), context_read(sessionID), subagent_cancel(sessionID).

    The scoping answer is yes: a plugin running in the orchestrating session can address child sessions by ID. The plugin session domain resolves sessions through the global session service with no ownership check (packages/core/src/plugin/host.ts L534–575), and the subagent tool returns the child's sessionID directly, so the parent can pass it straight to supervision tools. One wrinkle: the plugin domain has no list/active, so the "running" state has to be folded from session.execution.* events via the plugin event bus — which works fine on 2.1.0.

    End-to-end test (isolated config, opencode run --standalone, background child running a 60-second shell task):

    • session_status(child) → state:"running" mid-task
    • context_read(child) → the child's prompt plus its live shell tool part, visible from the parent
    • subagent_cancel(child) → {"interrupted":true}; ~0.4 s later session_status → state:"idle", outcome:"interrupted"

    So the supervision subset (status / context read / cancel) is confirmed deliverable as a plugin today, exactly as you sketched. Still blocked on core, per your analysis: message listing, fork, and the inbox — the harvest + fork + prompt path that actually recovers finished-but-unwritten work in #59565. One related note for scoping: without list/active in the plugin domain, a parent that loses a child's sessionID cannot rediscover it.

    Worth noting the flip side of that open resolution: as written, the tools act on any session the server knows, not just the caller's own children — a production version would scope them (the tool execution context carries the calling session's ID, and SessionInfo.parentID is available for the check). Two more core-side notes from reviewing this prototype: session.remove cascades to all of the target's child sessions, and the plugin API has no per-call permission ask — so a safe delete path would need core support as well.

    A finding for future plugin authors, hit during the first full run: a tool result that includes an output field must come from a tool that declared an output JSON Schema in editor.add, otherwise the result is rejected with Tool result declared output without an output schema (packages/core/src/tool/runtime.ts L46–47). The execute() side runs fine — only the result is dropped, which makes it a confusing failure mode to debug.

    Happy to share the plugin source if that's useful.

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

Metadata

Metadata

Assignees

No one assigned

    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