Skip to content

feat(app): add subagents tab to the session side panel - #39382

Open
sdpfigueiredo wants to merge 20 commits into
anomalyco:devfrom
sdpfigueiredo:sub-agents-tab
Open

feat(app): add subagents tab to the session side panel#39382
sdpfigueiredo wants to merge 20 commits into
anomalyco:devfrom
sdpfigueiredo:sub-agents-tab

Conversation

@sdpfigueiredo

@sdpfigueiredo sdpfigueiredo commented Jul 28, 2026

Copy link
Copy Markdown

Issue for this PR

Closes #37267

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Adds a "Subagents" tab to the session side panel, so subagent activity can be
followed without it being buried by the main agent's log stream, the problem
described in #37267.

The tab lists each subagent spawned by the current session with its task name,
status, cost, token usage and model. Clicking a row opens that subagent's session,
which is the drill-down to its full output.

Status comes from two places. Running/retrying is read from session_status in the
sync store. Terminal states (completed / cancelled / failed) come from the parent
session's task tool parts, the same source the CLI footer already uses in
packages/opencode/src/cli/cmd/run/subagent-data.ts. The parent session is the one
you have open, so its parts are already in the store and nothing extra is fetched.

The child list is derived from the live sync store filtered by parentID, plus a
one-shot backfill query for subagents created before the store's paginated window.
No polling and no per-subagent requests.

One part of the issue is not covered: there's no brief output summary on each row.
Rows show status and usage, and you click through for the output. Happy to add a
summary line if that's wanted.

How did you verify your code works?

bun run test:unit and bun run test:solid in packages/app (741 tests).

The one worth looking at is packages/app/test/session-agents-tab.test.tsx: it
mutates only session_status for one child and asserts the row's text changes while
the DOM node stays identical. It fails if the row values stop being reactive, which
is the mistake that makes this kind of list silently go stale.

Manually: started the backend and the web app, spawned several subagents in one
session, cancelled some mid-run and let others finish. The tab was opened once and
never reopened, new subagents appeared on their own and statuses changed in place.

Screenshots / recordings

01-FIXED-same-session-two-cancelled-rows 03-delta-live-running-Executando

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Adds a session side-panel tab listing the active session's subagents,
each showing task name, live status, cost, and token usage.

- Children are derived reactively from the live sync store plus a
  one-shot backfill for subagents created before the store's fetch
  window, deduped by id with the live entry winning.
- Per-row status and token totals are reactive accessors, so the tab
  reflects live updates without remounting.
- Terminal status (completed/cancelled/error) is read from the parent
  session's task tool parts, mirroring the CLI footer's reader, with
  zero per-child network requests.
- Cost and token formatting is shared with the session context tab via
  an extracted formatter module.
- i18n keys are complete and consistent across all 18 locales.
- taskStatus() now also treats a completed task part whose output
  begins with "Aborted" or "Task aborted" as cancelled, fixing a live
  QA finding where a subagent cancelled through a task-tool
  implementation that swallows aborts still rendered as Completed.
- Removed dead useSDK import/variable in the subagents tab.
- Removed dead useSync import/variable and fixed a floating promise
  in the session side panel.
- Removed two redundant `as Session[]` assertions on empty-array
  literals in the subagents context, proven redundant by typecheck.
- Fixed a TypeScript narrowing gap on session.cost inside a Show
  block, mirroring the existing session.model narrowing pattern.
- session.cost's Show block now gates on a plain boolean
  (session.cost != null) and coalesces with ?? 0 at the call site,
  instead of passing the raw number into Show's when - Solid gates
  on plain JS truthiness, so a cost of exactly 0 was being hidden by
  the previous fix instead of rendering $0.00.
- Added a DOM-level test asserting cost=0 renders correctly.
- Awaited a mock.module call in the pure-function test file that was
  still bare, matching the pattern used everywhere else in this
  branch's test files.
# Conflicts:
#	packages/app/src/i18n/bs.ts
#	packages/app/src/i18n/da.ts
#	packages/app/src/i18n/de.ts
#	packages/app/src/i18n/es.ts
#	packages/app/src/i18n/fr.ts
#	packages/app/src/i18n/ja.ts
#	packages/app/src/i18n/ko.ts
#	packages/app/src/i18n/no.ts
#	packages/app/src/i18n/pl.ts
#	packages/app/src/i18n/ru.ts
#	packages/app/src/i18n/th.ts
#	packages/app/src/i18n/tr.ts
#	packages/app/src/i18n/uk.ts
#	packages/app/src/i18n/zh.ts
#	packages/app/src/i18n/zht.ts
@liaozhenlin

Copy link
Copy Markdown

Many thanks to @sdpfigueiredo for implementing this! I see this PR addresses the issue I described in #37267, and the implementation looks good to me. From a functional perspective, I have no objections to this implementation. If it gets merged and released, I will test it out as soon as possible and provide feedback.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE]: Add a dedicated view for sub‑agent outputs and status in the desktop app

2 participants