Skip to content

feat(web): show subagent task trace in tool dialog#539

Merged
tiann merged 3 commits intotiann:mainfrom
junmo-kim:feature/web-task-trace-section
Apr 28, 2026
Merged

feat(web): show subagent task trace in tool dialog#539
tiann merged 3 commits intotiann:mainfrom
junmo-kim:feature/web-task-trace-section

Conversation

@junmo-kim
Copy link
Copy Markdown
Contributor

Problem

When a session uses the Task (subagent) tool, the tool dialog currently
shows only Input and Result. The child tool calls that run inside the
subagent (Glob, Grep, Read, …) are already wired through the reducer into
block.children, but the modal never renders them, leaving the user with
no visibility into what the subagent actually did.

Solution

Adds a Trace section between Input and Result in the Task tool dialog.
The section surfaces the existing block.children array without any new
data plumbing.

  • Default open/close policy mirrors intent-at-open: running and error
    expand (user opened the dialog to see progress or diagnose a failure);
    completed collapses (Result is the primary content, Trace is secondary).
  • Summary header shows (N calls · M.Mk tok · S.Ss), reading
    result.totalToolUseCount / totalTokens / totalDurationMs with
    graceful fallback when any field is absent.
  • Per-row inline expand lets the user drill into a child's Input/Result
    without opening a nested Dialog.
  • Type-guarded access to result.* fields — the field is typed unknown
    at the call site, so all numeric reads go through isObject guards.
  • Empty-children guard — the section does not render when there are no
    child tool calls, so non-Task tools are unaffected.

Screenshots

Running — Trace expanded, last child live (●) Completed — Trace collapsed, summary header shows totals
pr-running.png pr-completed.png
Error — Trace expanded, failing child marked (✕) Child inline expand — first child's result rendered in place
pr-error.png pr-child-expanded.png

Tests

14 new unit tests in web/src/components/ToolCard/trace.test.tsx:

  • getTaskTraceChildren — null for empty children, filters non-tool-call nodes
  • getTraceSummaryText — all three fallback branches
  • TraceSection — empty renders nothing; children render header; running/error
    default to expanded; completed defaults to collapsed; toggle inverts state;
    summary text visible in header

All existing tests continue to pass (bun run test, 187 total).

i18n

tool.trace key added for en and zh-CN.

Task tool modals previously showed only Input and Result. This adds a
Trace section between them that surfaces the child tool calls already
wired through the reducer into block.children.

- TraceSection collapses by default when completed, expands when
  running or error so the relevant state is visible on open
- Each child row toggles an inline expand (Input/Result) to avoid
  nested Dialogs
- Header summarises call count, token total and duration via
  readSummaryFields() typed parser, falling back gracefully when any
  value is absent
- formatTaskChildLabel / TaskStateIcon imported from shared helpers.tsx
  (extracted in prior refactor commit) — no local duplicates
- Task name guard: getTaskTraceChildren returns null for non-Task blocks
- children prop renamed to items in TraceSectionInner / TraceChildList
  (react/no-children-prop anti-pattern removed)
- i18n: tool.trace and tool.trace.callsSuffix keys added for en and
  zh-CN; useTranslation hooked up to header label and calls suffix
- 15 unit tests: getTaskTraceChildren (guard, filter, non-Task null),
  getTraceSummaryText (3 branches), TraceSection (open/close/toggle/
  summary/empty)
Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Findings

  • [Minor] Expanded trace rows omit child input — expanding a child call currently renders only the result, so calls whose important details live in input (for example Bash.command, Edit.old_string/new_string, or Write.content) still cannot be inspected from the Task trace. Evidence: web/src/components/ToolCard/trace.tsx:216.
    Suggested fix:
    const FullInputView = getToolFullViewComponent(child.tool.name)
    const { t } = useTranslation()
    
    {expanded && (
        <div className="ml-8 flex flex-col gap-2 rounded border border-[var(--app-border)] p-2">
            <div>
                <div className="mb-1 text-xs font-medium text-[var(--app-hint)]">{t('tool.input')}</div>
                {FullInputView ? (
                    <FullInputView block={child} metadata={metadata} />
                ) : (
                    <CodeBlock code={safeStringify(child.tool.input)} language="json" />
                )}
            </div>
            <div>
                <div className="mb-1 text-xs font-medium text-[var(--app-hint)]">{t('tool.result')}</div>
                <ResultView block={child} metadata={metadata} />
            </div>
        </div>
    )}

Questions

  • None.

Summary

  • Review mode: initial
  • One functional gap found in the new trace UI. Residual risk: I could not execute the test suite in this runner.

Testing

  • Not run (automation): bun is not installed in the runner (bun: command not found).

HAPI Bot

Comment thread web/src/components/ToolCard/trace.tsx Outdated

{expanded && (
<div className="ml-8 flex flex-col gap-2 rounded border border-[var(--app-border)] p-2">
<ResultView block={child} metadata={metadata} />
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[MINOR] Expanded trace rows omit child input

TraceChildRow only renders <ResultView block={child} ... /> when a child is expanded. That leaves important call details hidden for tools where the meaningful data is in child.tool.input, such as Bash.command, Edit.old_string/new_string, or Write.content, so the trace still does not let users inspect what the subagent actually invoked.

Suggested fix:

const FullInputView = getToolFullViewComponent(child.tool.name)
const { t } = useTranslation()

{expanded && (
    <div className="ml-8 flex flex-col gap-2 rounded border border-[var(--app-border)] p-2">
        <div>
            <div className="mb-1 text-xs font-medium text-[var(--app-hint)]">{t('tool.input')}</div>
            {FullInputView ? (
                <FullInputView block={child} metadata={metadata} />
            ) : (
                <CodeBlock code={safeStringify(child.tool.input)} language="json" />
            )}
        </div>
        <div>
            <div className="mb-1 text-xs font-medium text-[var(--app-hint)]">{t('tool.result')}</div>
            <ResultView block={child} metadata={metadata} />
        </div>
    </div>
)}

Expanded child rows in the Task trace section now render both an Input
section and a Result section, matching the pattern used in the parent
ToolCard dialog. Tools with a registered FullInputView use it; all
others fall back to a JSON CodeBlock. Closes bot review on PR tiann#539.
Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Findings

  • None.

Questions

  • None.

Summary

  • Review mode: follow-up after new commits
  • No blocking correctness, security, regression, data loss, performance, or maintainability issues found in the latest diff. The follow-up commit addresses the prior bot finding by rendering expanded child input along with result output. Residual risk: validation is based on static review only.

Testing

  • Not run (automation; PR code not executed during review)

HAPI Bot

@tiann tiann merged commit 52ec08b into tiann:main Apr 28, 2026
2 checks passed
@junmo-kim junmo-kim deleted the feature/web-task-trace-section branch April 29, 2026 01:41
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.

2 participants