Skip to content

fix(chat): match sub-agent card + composer to main-chat/Studio, freeze on stop - #3644

Merged
kwakayama merged 7 commits into
mainfrom
mattboon/fix-agent-card-bugs
Aug 13, 2026
Merged

fix(chat): match sub-agent card + composer to main-chat/Studio, freeze on stop#3644
kwakayama merged 7 commits into
mainfrom
mattboon/fix-agent-card-bugs

Conversation

@mattboon

@mattboon mattboon commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Fixes a batch of agentic-chat UI bugs surfaced in the agentic-case-processing example. The invoke_agent sub-agent card stays a card, but its insides now render with the same primitives as the main chat (per design direction).

Fixes

# Bug Fix
1 Avatar not rendered in the invoke_agent card Render the shared Avatar (image + initial fallback); plumb child agentName/avatarUrl through the child stream so it shows while running, not only on completion
2 Pressed Stop, card + skill row stayed "Running"/shimmering Freeze a still-running card and its live child rows to Stopped once the turn stops streaming without a terminal result. New SkillTool stopped state + stopped tool badge
3 No JSON syntax highlighting inside the subagent tool card Child tools route through the standard ToolCall card; parse streamed child inputTextinput so params highlight like top-level tools
4 Instructions / Thought process styled differently than main chat Replaced the hand-rolled <details> blocks with the main-chat Reasoning disclosure (Studio parity)
5 / 6 Extra space below "Thought process" / below the avatar Removed the additive mb-3 on Reasoning.Root that fought the flex gap (Studio has none)
7 Error banner looked bad Match Studio's inline retry banner — amber warning alert + triangle icon + filled "Try again" button; add real --alert-*-border tokens instead of a washed-out color-mix border
8 "+" menu too verbose ("Attach files to chat") + opens the file dialog directly when attach is the only action (no 1-item dropdown); menu item renamed "Add photos & files" (ChatGPT parity)
9 Attachment pill floated above the composer Move pending-attachment pills inside the composer card, above the field (Studio)
10 Scroll-to-bottom button styling weird Cleaner floating circle lifted off the transcript

Notes on the Stop path

Server-side cancellation is already wired end-to-end (useChat().stop() -> fetch abort -> run-stream.ts stream cancel() -> sessionManager.cancelRun -> run abortSignal -> default-invoke-agent-tool throwIfChildRunAborted + forwarded abortSignal -> child fork -> child agent.stream({ abortSignal })). The visible "still Running" defect was purely client-side: already-received tool parts never get a terminal state after the fetch aborts. This PR fixes that client rendering; no server change needed.

The client has no explicit "this child was cancelled" event, so the frozen state is inferred - but only where the inference holds. resolveInterruption reads the chat lifecycle rather than the mere absence of streaming:

Situation Card reads
Chat still submitted / streaming (the turn moved on to another message) Running
Chat ready, tool never resolved (user pressed Stop) Stopped
Chat error (stream died mid-run) Failed
Tool in approval-requested / approval-responded (paused on a person) its own state, untouched
No ChatContext / no MessageContext (standalone card) nothing inferred

A hand-built ChatContext that omits status falls back to the message's own streaming flag.

Breaking changes (public API)

Small, but they change behavior for existing callers:

  • ErrorBanner icon moved from the retry button to the alert's leading icon slot. A caller passing icon now sees it beside the message, not on the button. Put a button glyph in retryLabel.
  • ErrorBanner default retryLabel is "Try again" (was "Retry").
  • The composer's + control is labelled "Add photos & files" (was "Add document"). Selectors and e2e specs keying on the old accessible name need updating.
  • Reasoning.Root no longer carries mb-3. Both in-repo hosts are flex-gap containers, so they tighten to the intended spacing; a custom non-gap layout should pass className.

Testing

  • deno check + deno lint clean on all changed files; deno fmt applied. fmt:check, docs:api-reference:check, lint:chat-composability, lint:chat-ratchets, lint:module-boundaries and lint:dependency-boundaries pass.
  • Suites: tool-ui, skill-tool, reasoning, error-banner, chat-composer, empty-state, invoke-agent-stream, composition, design-tokens, alert, plus the full src/react/, src/chat/ and src/agent/react/ trees (214 files, 1545 steps, green).
  • New coverage for what previously had none:
    • identity end to end - composition publishes name + avatar on every event (so the header is right while it runs, not only at the end) and honours the deprecated avatar_url; invoke-agent-stream.test.ts (a new file - the module had no tests) covers identity parse, snapshot merge, and the merge keeping identity once the runtime stops repeating it; tool-ui asserts the streamed name beats the humanized id and the avatar renders mid-run.
    • the three interruption paths above.
    • the onAttach-only branch opening the picker with no dropdown, and pending pills rendering inside the composer card and removing.
    • the stopped skill row, and an --alert-*-border token for every fill in both color modes.
  • Rendered all seven changed states to a page with the real design tokens + the storybook Tailwind build and reviewed the screenshot: running / stopped / errored-turn / still-streaming cards, the amber banner, the composer with a pending pill, and the scroll button. (Storybook's dev server is currently broken in this checkout - node:async_hooks is externalized for the browser via src/react/head-collector.ts, which fails on unmodified stories too. Unrelated to this PR, worth its own issue.)

Design source of truth cross-checked against veryfront-studio (InvokeAgentTool, Reasoning, Alert, AgentAvatar). The light alert border tokens match veryfront-studio/styles/styles.css exactly (#F5BA67 / #E06E7B / #6FB57C / #ADADAA); Studio has no dark-mode alert overrides, so the dark pair is derived here at 40% of the status color.

Scope note

buildInvokeAgentStreamDataEvent has exactly one caller, runAgentAsStreamingTool (the agentAsTool / createInvokeAgentTool composition path). The hosted default-invoke-agent-tool mirrors child output separately and does not publish these events, so the avatar/name fix lands on the composition path this example uses, not on hosted child cards.

Summary by CodeRabbit

  • New Features

    • Child-agent cards now show the agent’s name and avatar when available.
    • Interrupted child-agent tasks remain visible with a clear “Stopped” state.
    • File-only attachments open the picker directly, with pending files shown inside the composer.
    • Updated attachment label to “Add photos & files.”
  • Bug Fixes

    • Improved handling of streamed child-agent instructions, reasoning, and tool inputs.
    • Error alerts now use clearer warning styling and a “Try again” action.
  • Style

    • Refined conversation controls, reasoning spacing, and alert borders across themes.

…e on stop

Fixes a batch of agentic-chat UI bugs surfaced in the agentic-case-processing
example. The `invoke_agent` sub-agent card stays a card, but its insides now
render with the same primitives as the main chat.

- invoke_agent card: render the shared `Avatar` (image + initial fallback) and
  plumb child `agentName`/`avatarUrl` through the child stream so the avatar
  shows while the child is running, not only on completion.
- invoke_agent card: replace the hand-rolled `<details>` Instructions/Thought
  process blocks with the main-chat `Reasoning` disclosure (Studio parity).
- invoke_agent card: parse streamed child `inputText` -> `input` so child tool
  params get the same JSON syntax highlighting as top-level tool cards.
- stop: freeze a still-running card + child rows to "Stopped" once the turn
  stops streaming without a terminal result (no forever-"Running", no perma
  skill shimmer). New `SkillTool` "stopped" state + `stopped` tool badge.
- reasoning: drop the additive `mb-3` on `Reasoning.Root` that fought the flex
  gap (extra space below "Thought process" / below the avatar).
- error banner: match Studio's inline retry banner — amber `warning` alert with
  a triangle icon and a filled "Try again" button; add real `--alert-*-border`
  tokens instead of a washed-out color-mix border.
- composer: `+` opens the file dialog directly when attach is the only action
  (no 1-item dropdown); menu item renamed "Add photos & files".
- composer: move pending-attachment pills inside the composer card, above the
  field (Studio), instead of floating as a separate row.
- scroll-to-bottom: cleaner floating circle lifted off the transcript.

Tests updated to the new anatomy; adds coverage for the stop-freeze. Verified
against the agentic-case-processing example running on a local worktree.
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d3a15997-034a-4142-b53a-7af4d18b3252

📥 Commits

Reviewing files that changed from the base of the PR and between 559fdb5 and b513bd6.

⛔ Files ignored due to path filters (1)
  • src/server/handlers/dev/framework-candidates.generated.ts is excluded by !**/*.generated.*
📒 Files selected for processing (16)
  • docs/api-reference/veryfront/agent.md
  • docs/api-reference/veryfront/chat.md
  • src/agent/composition/composition.test.ts
  • src/agent/composition/composition.ts
  • src/chat/invoke-agent-stream.test.ts
  • src/chat/invoke-agent-stream.ts
  • src/react/components/chat/chat/components/empty-state.tsx
  • src/react/components/chat/chat/components/reasoning.tsx
  • src/react/components/chat/chat/components/skill-tool.test.tsx
  • src/react/components/chat/chat/components/skill-tool.tsx
  • src/react/components/chat/chat/components/tool-ui.test.tsx
  • src/react/components/chat/chat/components/tool-ui.tsx
  • src/react/components/chat/chat/composition/chat-composer.test.tsx
  • src/react/components/chat/chat/composition/chat-input-attach.tsx
  • src/react/components/chat/chat/composition/error-banner.tsx
  • src/react/components/ui/design-tokens.test.ts
🚧 Files skipped from review as they are similar to previous changes (10)
  • docs/api-reference/veryfront/agent.md
  • src/agent/composition/composition.ts
  • src/react/components/chat/chat/components/reasoning.tsx
  • docs/api-reference/veryfront/chat.md
  • src/react/components/chat/chat/components/tool-ui.tsx
  • src/react/components/chat/chat/components/skill-tool.tsx
  • src/chat/invoke-agent-stream.ts
  • src/react/components/chat/chat/composition/chat-input-attach.tsx
  • src/react/components/chat/chat/components/empty-state.tsx
  • src/react/components/chat/chat/composition/error-banner.tsx

📝 Walkthrough

Walkthrough

The PR propagates child-agent identity through streams, freezes interrupted child tools, extracts streamed tool input, updates composer and error presentation, and adds dedicated alert border tokens. It also adjusts chat spacing, tests, Storybook themes, and API references.

Changes

Child-agent stream and rendering

Layer / File(s) Summary
Stream identity propagation
src/chat/invoke-agent-stream.ts, src/agent/composition/composition.ts, src/chat/invoke-agent-stream.test.ts, src/agent/composition/composition.test.ts
Child-agent events and snapshots preserve validated names and avatar URLs, including the avatar_url fallback.
Child-agent card rendering
src/react/components/chat/chat/components/tool-ui.tsx
Child-agent cards use streamed identity, shared reasoning components, recovered tool input, and interrupted-turn handling.
Stopped skill-tool state
src/react/components/chat/chat/components/skill-tool.tsx, src/react/components/chat/chat/components/skill-tool.test.tsx
Active skill tools render static “Stopped” rows when streaming ends before completion.
Child-agent rendering validation
src/react/components/chat/chat/components/tool-ui.test.tsx
Tests cover streaming visibility, streamed input recovery, lifecycle states, identity rendering, and stopped child tools.

Chat composer and error presentation

Layer / File(s) Summary
Composer attachment flow
src/react/components/chat/chat/composition/chat-composer.tsx, src/react/components/chat/chat/composition/chat-input-attach.tsx, src/react/components/chat/chat/composition/chat-composer.test.tsx
Attachment controls support direct file selection or a controlled menu. Pending attachments render inside the composer card.
Error banner presentation
src/react/components/chat/chat/composition/error-banner.tsx, src/react/components/chat/chat/composition/error-banner.test.tsx
Error banners use warning styling, triangle icons, compact retry buttons, and “Try again” by default.

Chat visual and alert styling

Layer / File(s) Summary
Chat component styling
src/react/components/chat/chat/components/empty-state.tsx, src/react/components/chat/chat/components/reasoning.tsx
The scroll button and reasoning wrapper use updated spacing and visual styles.
Alert border token integration
src/react/components/ui/alert.tsx, src/react/components/ui/design-tokens.ts, storybook/.storybook/preview.css, src/react/components/ui/alert.test.tsx, src/react/components/ui/design-tokens.test.ts
Alert variants use dedicated border tokens for light and dark themes. Tests verify the variant tokens.

API reference synchronization

Layer / File(s) Summary
Agent, chat, and UI API references
docs/api-reference/veryfront/agent.md, docs/api-reference/veryfront/chat.md, docs/api-reference/veryfront/ui.md
API reference descriptions and source links point to the updated implementations.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Mergeability Score: 🟡 Moderate · up to b513b

The PR is not merge-ready because duplicate declarations in the composition test file prevent the test suite from compiling; remove the duplicates before merging.

Sequence Diagram(s)

sequenceDiagram
  participant AgentConfiguration
  participant ChildAgentStream
  participant InvokeAgentStream
  participant ChildAgentCard
  participant ChildTool
  AgentConfiguration->>ChildAgentStream: publish identity metadata
  ChildAgentStream->>InvokeAgentStream: deliver streamed values
  InvokeAgentStream->>ChildAgentCard: provide identity and snapshot data
  ChildAgentCard->>ChildTool: render streamed input and status
  ChildAgentCard->>ChildTool: freeze active tool as Stopped when streaming ends
Loading

Possibly related PRs

Suggested reviewers: kojiwakayama, kwakayama

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 79.17% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes to the sub-agent card, composer, and stop-state behavior.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch mattboon/fix-agent-card-bugs

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 63358a07c7

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +15 to +18
default: "bg-[var(--alert-info-bg)] border-[var(--alert-info-border)]",
warning: "bg-[var(--alert-warning-bg)] border-[var(--alert-warning-border)]",
error: "bg-[var(--alert-error-bg)] border-[var(--alert-error-border)]",
success: "bg-[var(--alert-success-bg)] border-[var(--alert-success-border)]",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Regenerate framework candidates for alert borders

In apps whose CSS is generated from FRAMEWORK_CANDIDATES, these new border-[var(--alert-*-border)] utilities are never emitted because src/server/handlers/dev/framework-candidates.generated.ts:7768-7771 still only safelists the old border-[color-mix(...--alert-*-bg...)] alert border classes. The dev scanner and production static generation both seed framework component CSS from that generated list, so Veryfront alerts lose the intended border unless the host app independently uses the same classes. Regenerate the candidate file with the new alert border utilities.

AGENTS.md reference: AGENTS.md:L13-L14

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Addressed. framework-candidates.generated.ts was regenerated (deno run -A scripts/build/prebundle-client-scripts.ts) and now safelists the new utilities:

+  "border-[var(--alert-error-border)]",
+  "border-[var(--alert-info-border)]",
+  "border-[var(--alert-success-border)]",
+  "border-[var(--alert-warning-border)]",
-  "border-[color-mix(in_oklch,var(--alert-error-bg),black_10%)]",

Regenerated again on the latest commit, so it also covers the scroll-button change. design-tokens.test.ts now asserts a --alert-*-border token for every fill in both color modes, so a fill added without its border token fails the suite rather than silently falling back to currentColor.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/react/components/chat/chat/components/tool-ui.tsx`:
- Line 416: Update the input resolution near the tool result construction to
prefer parsed non-empty existing.inputText over the empty {} placeholder in
existing.input, while preserving genuinely structured existing.input values. Add
a focused regression test covering the tool-input-start → tool-input-delta →
tool-output-available sequence and verify the streamed JSON appears in the
Parameters block.

In `@src/react/components/chat/chat/composition/chat-composer.test.tsx`:
- Line 343: Add focused tests in the chat composer test suite for attachment
flows: verify the direct picker works when only onAttach is provided, and verify
pending attachments render inside the card and can be removed. Reuse the
existing attachment button/menu selectors and preserve the current combined-menu
coverage.

In `@src/react/components/chat/chat/composition/chat-composer.tsx`:
- Around line 149-153: Update the menu trigger’s aria-label near the c.onAttach
and c.onSelectAttachment handling to use “Add photos & files” whenever
c.onAttach is available, while retaining “Add document” for document-only menus.

In `@src/react/components/chat/chat/composition/error-banner.test.tsx`:
- Line 20: Update the retry-label test to pass a custom value distinct from the
default “Try again,” such as “Retry now,” and assert that custom value in the
rendered HTML. Keep the test focused on the retryLabel override behavior.

In `@src/react/components/ui/alert.tsx`:
- Around line 14-18: Add focused regression coverage in alert.test.tsx for
alertVariants, asserting each default, warning, error, and success variant
renders its matching --alert-{variant}-border class. Keep the existing
foreground assertions and use the test’s established rendering pattern.

In `@storybook/.storybook/preview.css`:
- Around line 56-62: Update the [data-theme="dark"] block in preview.css to
override all four alert border tokens—warning, error, success, and info—with the
corresponding dark-theme values from design-tokens.ts. Keep the existing root
definitions and other dark-theme overrides unchanged.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8472debb-fdc9-4545-8316-eb5da3b00fab

📥 Commits

Reviewing files that changed from the base of the PR and between 870bcde and 63358a0.

📒 Files selected for processing (14)
  • src/agent/composition/composition.ts
  • src/chat/invoke-agent-stream.ts
  • src/react/components/chat/chat/components/empty-state.tsx
  • src/react/components/chat/chat/components/reasoning.tsx
  • src/react/components/chat/chat/components/skill-tool.tsx
  • src/react/components/chat/chat/components/tool-ui.test.tsx
  • src/react/components/chat/chat/components/tool-ui.tsx
  • src/react/components/chat/chat/composition/chat-composer.test.tsx
  • src/react/components/chat/chat/composition/chat-composer.tsx
  • src/react/components/chat/chat/composition/error-banner.test.tsx
  • src/react/components/chat/chat/composition/error-banner.tsx
  • src/react/components/ui/alert.tsx
  • src/react/components/ui/design-tokens.ts
  • storybook/.storybook/preview.css

Comment thread src/react/components/chat/chat/components/tool-ui.tsx Outdated
Comment thread src/react/components/chat/chat/composition/chat-composer.test.tsx
Comment thread src/react/components/chat/chat/composition/chat-composer.tsx Outdated
Comment thread src/react/components/chat/chat/composition/error-banner.test.tsx
Comment thread src/react/components/ui/alert.tsx
Comment thread storybook/.storybook/preview.css
mattboon and others added 5 commits August 12, 2026 21:03
- Regenerate framework-candidates.generated.ts so the new
  `border-[var(--alert-*-border)]` alert utilities are safelisted (fixes the
  "framework-candidates is stale" CI typecheck/lint failure and the alert
  border tokens actually emit in generated CSS).
- tool-ui: prefer parsed streamed child `inputText` over the `{}` placeholder in
  the tool-output-available branch (recovers params for the
  start -> delta -> output sequence); add a focused regression test.
- composer: give the combined attach menu trigger the "Add photos & files"
  accessible name when file-attach is available (a11y).
- alert: add a regression test asserting each variant's fill + border token pair.
- storybook: add dark-theme alert bg + border token overrides.
- error-banner test: use a distinct custom retry label so it verifies the override.
Splitting the composer's + attachment control into chat-input-attach.tsx keeps
chat-composer.tsx under its ban-chat-antipatterns LOC ceiling (338 < 389) after
the single-action + attachments-inside-card changes. Public API unchanged:
ChatInputAttach is re-exported and still wired as ChatInput.Attach.
Extracting ChatInput.Attach into its own file changed the extracted framework
component candidate set; regenerate so framework-candidates.generated.ts is
current (fixes the stale-file CI typecheck/lint failure).
Updates veryfront/{agent,chat,ui}.md source line anchors / summaries changed by
this branch (alert doc, error-banner default label, Attach extraction). Fixes
the docs:api-reference:check step of lint:ci.
…its absence

Review follow-ups on the sub-agent card work.

The card inferred "Stopped" from `!isStreaming && !terminal`. That fires on
every terminal path, so a turn killed by a stream error read as a user stop,
and a tool paused on a human (`approval-requested` / `approval-responded`) read
as stopped while it waited. Resolve the interruption from the chat status
instead: still submitted/streaming means the turn only moved on to another
message, `error` reports a failure, and an approval-gated tool keeps its own
state. A provider that omits `status` falls back to the previous behavior.

Also:
- Publish the child identity once per run instead of rebuilding it for every
  streamed chunk.
- A stopped skill row reads "Stopped loading skill: X", not a frozen "Loading".
- Size the scroll-to-bottom button by padding so a custom icon is not clipped.
- Move the attach menu's open state into the branch that renders the menu.
- Document the `ErrorBanner.icon` slot move and the `Reasoning.Root` margin
  removal on the props themselves.

Tests: the identity plumbing end to end (composition publish, contract parse
and snapshot merge, card header render), the three interruption paths, the
direct file-picker branch, pending pills inside the composer card, the stopped
skill row, and an alert border token for every fill.
@kwakayama

Copy link
Copy Markdown
Contributor

Review follow-ups pushed in b513bd6.

Stop detection was the real one. !isStreaming && !terminal fires on every terminal path, not just a user stop. useChat clears streamingMessageId in its finally for errors too (use-chat.ts:452, alongside setStatus("error")), so a turn killed by a stream error read as "Stopped"; and approval-requested / approval-responded are real wire states (src/chat/types.ts:126), so an approval-gated child read as "Stopped" while it waited on a person. resolveInterruption now reads the chat lifecycle: still submitted/streaming -> Running, error -> Failed, awaiting a human -> its own state, otherwise Stopped. Providers without status keep the old behavior.

Also fixed: identity was rebuilt and re-sent on every streamed chunk (now resolved once per run); a stopped skill row read "Loading skill: X" forever (now "Stopped loading skill: X"); the scroll button clipped a custom icon at a fixed size-8 (padding-sized now); dead menu state in the direct-picker branch.

Tests for what had none. src/chat/invoke-agent-stream.ts had no test file at all, and nothing covered the avatar fix - the first bug in the table. Added identity coverage end to end, the three interruption paths, the onAttach-only picker branch, pending pills inside the composer card, the stopped skill row, and an --alert-*-border assertion per fill. Full src/react/, src/chat/, src/agent/react/ trees green (214 files, 1545 steps).

Breaking changes are now called out in the description - ErrorBanner.icon moving slots, the retryLabel default, the + accessible name, and Reasoning.Root losing mb-3.

Rendered all seven changed states with the real tokens + storybook Tailwind build and checked the screenshot before saying they look right. Storybook dev is broken in this checkout (node:async_hooks externalized via src/react/head-collector.ts) - it fails on unmodified stories too, so it predates this PR and deserves its own issue.

`ci (lint)` caught an invalid cast: `ToolExecutionDataEvent` carries an
`unknown` index signature, so asserting on `event as { value: {...} }` does not
typecheck. Parse the published value with `parseInvokeAgentStreamValue`
instead, which also makes the assertion go through the real contract rather
than a shape the test asserts into existence. The snake_case case needed a
typed stream event to parse, so it no longer relies on the bare `data: {}`
default.

Worth noting for the next reader: this one error made `lint:test-typecheck`
split the repository-wide clean check into batches, and four unrelated files
(the RequestInit ones in workflow/transforms/use-chat) then reported failures
that never appear when the whole test surface is checked in one pass. They are
an artifact of the split, not new rot -- the gate is back to "51 grandfathered
files, 0 new" once this typechecks.
@kwakayama
kwakayama added this pull request to the merge queue Aug 13, 2026
Merged via the queue into main with commit f83c088 Aug 13, 2026
33 checks passed
@kwakayama
kwakayama deleted the mattboon/fix-agent-card-bugs branch August 13, 2026 07:55
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