Skip to content

feat: derive sub-agent state and surface delegated work - #60

Merged
leoncheng57 merged 3 commits into
mainfrom
opencode/shiny-eagle
Aug 22, 2026
Merged

leoncheng57 merged 3 commits into
mainfrom
opencode/shiny-eagle

Conversation

@leoncheng57

@leoncheng57 leoncheng57 commented Aug 22, 2026

Copy link
Copy Markdown
Owner

Closes #46. Implements the state model and orchestration surface in #51.

Why

OpenCode has child sessions but no durable background-job API, so "what are this session's sub-agents doing?" cannot be read — it has to be derived. Until now the runner had the primitives (parentID, subagent_depth) and used none of them: children were invisible as children, a delegation was an opaque task chip, and a background hand-back rendered as a chat bubble the human never typed. In one audited project 124 of 149 sessions were children, so the flat list was ~83% noise.

The state model

server/opencode/subagents.ts reconciles four upstream facts into one ledger keyed by child session id — never by task-part id, since resume emits several parts per child:

Evidence Strength
/session/status reports busy Observed, process-local
The child's own final turn First-hand terminal
A hand-back notice in the parent Second-hand but explicit
The delegating task part Weakest, and conditional

Precedence follows that order. A background task part flips to completed when the launch call returns, typically long before the child finishes, so it is never terminal evidence. A synchronous one blocked on the child and is.

When nothing settles a child — cancelled, or the agent server restarted mid-run — the row is unknown and says what was checked. The audit found three cancelled children with no parent notification at all; rendering those as "completed" would be a confident falsehood, so the UI admits it instead. Every row shows its provenance.

Surfaces

  • Hub lists roots depth-first with their delegated work, a sub pill, and a direct child count. Nested delegation and children whose parent is archived or elsewhere are both preserved — the flatten emits every session exactly once, including through a corrupt parent cycle.
  • Child transcript badges itself and links back to the parent that delegated it.
  • Task rows link to the child that ran them, and are excluded from collapsed action groups so that route is never hidden behind a chevron.
  • Hand-back notices render as status rows instead of prompts attributed to the user. Detection requires a child session id and an outcome word.
  • Subagents inspector tab lists every child with state, evidence, agent, cost and a link.
  • Deep links can open ?panel=subagents; on mobile this opens the full-screen inspector sheet.

Controls promise only what the connected process can deliver

Stop appears solely for children /session/status reports busy, because abort authority is process-local. Background promotion is gated on /experimental/capabilities, not on an env var the BFF cannot read. Child endpoints verify the parent link first.

Configuration and settings

  • Sets this repository's subagent_depth to 3. OpenCode defaults to 1, which prevents nested delegation.
  • Displays the global depth read-only on Settings; project-level authoring remains in opencode.json.
  • Represents inherited compaction booleans explicitly as Default (on/off) instead of incorrectly rendering an unset value as off.
  • Keeps inherited values out of PATCH bodies so saving does not silently pin defaults.

Cost

One child-list and one status call, the newest parent page for intent, and child transcripts probed only for children that are neither running nor already settled — newest first, capped at 12, concurrency-limited, with truncated reported rather than silently implied.

Notes for review

Verification

  • npm run typecheck
  • npm test (356 passed)
  • npm run build
  • npm run test:e2e (192 passed, 1 expected screenshot-runner skip)
  • npm run screenshots:local (8 routes; desktop + mobile)
full:/?directory=/tmp/mock-subagent-project
full:/sessions/ses_mock_parent?directory=/tmp/mock-subagent-project
full:/sessions/ses_mock_child_running?directory=/tmp/mock-subagent-project
full:/sessions/ses_mock_parent?directory=/tmp/mock-subagent-project&panel=subagents
full:/settings

OpenCode delegates to child sessions but exposes no durable background-job
API, so sub-agent state has to be derived. server/opencode/subagents.ts
reconciles four upstream facts — the child list, the parent's task parts,
the process-local status map, and each child's own transcript — into one
ledger keyed by child session id, never by task-part id.

Precedence is fixed and each row reports the evidence behind it, because
the sources differ sharply in strength. A background task part reporting
"completed" only means the launch call returned, so it is never read as a
finished child; a synchronous one blocked on the child and is. When nothing
settles a child — cancelled, or the agent server restarted mid-run — the row
is `unknown` and names what was checked rather than guessing.

Surfaces:
- Hub lists roots depth-first with their delegated work, a `sub` pill and a
  direct child count. Nested delegation and orphaned children are both
  preserved; nothing is dropped or duplicated.
- A child transcript badges itself and links back to the parent that
  delegated it.
- Task rows link to the child that ran them, and stay out of collapsed
  action groups so that route is never hidden.
- A background hand-back — injected as a user-role message — renders as a
  status row instead of a prompt the human never typed.
- An Agents inspector tab lists every child with state, evidence and cost.

Controls only promise what the connected process can deliver: Stop appears
solely for children reported busy, background promotion is gated on
/experimental/capabilities, and child endpoints verify the parent link
before acting so they cannot be used as a general-purpose abort.

Cost is bounded: one child-list and status call, the newest parent page for
intent, and child transcripts probed only for children that are neither
running nor already settled — newest first, capped, concurrency-limited,
with truncation reported rather than implied.
github-actions Bot added a commit that referenced this pull request Aug 22, 2026
github-actions Bot added a commit that referenced this pull request Aug 22, 2026
github-actions Bot added a commit that referenced this pull request Aug 22, 2026
@leoncheng57
leoncheng57 merged commit 576d6e8 into main Aug 22, 2026
3 of 4 checks passed
github-actions Bot added a commit that referenced this pull request Aug 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant