Skip to content

feat(a2ui): chat add action request and response#2667

Merged
Sherry-hue merged 1 commit into
lynx-family:mainfrom
Sherry-hue:feat/chat-action
May 19, 2026
Merged

feat(a2ui): chat add action request and response#2667
Sherry-hue merged 1 commit into
lynx-family:mainfrom
Sherry-hue:feat/chat-action

Conversation

@Sherry-hue
Copy link
Copy Markdown
Collaborator

@Sherry-hue Sherry-hue commented May 19, 2026

Summary by CodeRabbit

  • New Features

    • Live action preview with dedicated action streaming and real-time response cards
    • Role-aware messages: action, status, and JSON message cards with inline payload rendering
    • Suggested prompt chips shown on fresh chats
  • Improvements

    • Auto-scroll/stable layout during streaming and editor resizing
    • Enhanced JSON display and payload formatting
    • Button labels now rendered via child text components and action payloads use nested event objects

Review Change Stack

Checklist

  • Tests updated (or not required).
  • Documentation updated (or not required).
  • Changeset added, and when a BREAKING CHANGE occurs, it needs to be clearly marked (or not required).

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 19, 2026

⚠️ No Changeset found

Latest commit: eddbe61

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 19, 2026

📝 Walkthrough

Walkthrough

This PR implements a complete action-streaming workflow for the A2UI playground. It restructures Button component schemas to use nested action payloads, threads a liveAction flag through initialization, adds an SSE action-stream endpoint, implements bridge message routing between playground frames, and builds chat UI for displaying action workflows with improved auto-scroll behavior.

Changes

LiveAction Action Streaming Feature

Layer / File(s) Summary
Button Component Schema Refactor
packages/genui/server/agent/a2ui-catalog.ts, packages/genui/server/agent/a2ui-prompt.ts, packages/genui/server/agent/a2ui-validator.ts
Button component changes from inline label to required child Text; action payload is restructured to nest event.name and optional event.context. Prompt examples and validation updated.
LiveAction Flag Initialization Pipeline
packages/genui/a2ui-playground/src/utils/renderUrl.ts, packages/genui/a2ui-playground/src/render.tsx, packages/genui/a2ui-playground/lynx-src/a2ui/App.tsx
Adds liveAction to RenderInit/InitData, parses from ?liveAction=1, normalizes and merges init layers, and forwards into globalProps/effective App data.
Conversation Threading & Recording Infrastructure
packages/genui/server/app/a2ui/stream/route.ts, packages/genui/server/service/a2ui-agent.ts
Resolves threadId when missing (randomUUID), and adds recordStreamedConversation to persist streamed interactions and A2UI messages into conversation state.
Action Stream SSE Endpoint
packages/genui/server/app/a2ui/action/stream/route.ts
New SSE POST/OPTIONS route that accepts action requests, enforces rate limits, synthesizes user-action messages, streams model deltas (start/delta/done), validates output, and conditionally records the conversation.
Native Bridge & Inter-frame Routing
packages/genui/a2ui-playground/src/render.tsx, packages/genui/a2ui-playground/lynx-src/a2ui/App.tsx
Forwards A2UI_USER_ACTION from the embedded app to parent/native bridge when liveAction is enabled; routes incoming A2UI_ACTION_RESPONSE from parent into the embedded lynx-view and injects normalized messages into the local MessageStore.
Chat Message Structure & Auto-scroll
packages/genui/a2ui-playground/src/pages/AIChatPage.tsx
Expands ChatMessage roles (action/json/status), adds safeStringifyPayload, captures threadId from SSE start, appends assistant json outputs, implements robust follow-bottom auto-scroll using ResizeObserver and MutationObserver, and wires A2UI action streaming from incoming postMessages.
Action Workflow & Message Rendering
packages/genui/a2ui-playground/src/pages/AIChatPage.tsx
Handles A2UI_USER_ACTION postMessage: validates/uses threadId, aborts prior action streams, POSTs to the action-stream endpoint, inserts placeholders, streams incremental payload into an action card, posts final applied messages back to preview, and updates status on success/failure. Renders role-aware messages and JSON payloads in read-only CodeMirror.
Chat UI Styling & Layout Updates
packages/genui/a2ui-playground/src/pages/AIChatPage.css
Adds flex sizing fixes (min-height, flex-shrink), action pill and pending spinner styles, status row variants, JSON editor sizing and theme token changes, suggestion chips, and refactors input area layout to vertical column with suggestion row.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • HuJean
  • gaoachao
  • fzx2666-fz

Poem

🐰 I stitched a tiny action stream,
Threads and messages in a seam.
Buttons nest their eventful name,
Chat scrolls true and keeps its frame.
A rabbit hops — the preview gleams.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 'feat(a2ui): chat add action request and response' clearly summarizes the main change—adding action request and response handling to the A2UI chat system.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@codecov
Copy link
Copy Markdown

codecov Bot commented May 19, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/genui/server/agent/a2ui-validator.ts (1)

282-289: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Enforce the new Button schema in validator, not just action.event.name.

Button now requires a child id and deprecates inline label, but this validator block only checks action.event.name. Schema-invalid Button payloads can still pass validation.

Suggested patch
         if (requiresAction.has(comp.component)) {
           const action = comp.action;
           if (!hasActionName(action)) {
             errors.push(
               `${comp.component} (id=${comp.id}) MUST carry action.event.name.`,
             );
           }
         }
+        if (comp.component === 'Button') {
+          if (typeof comp.child !== 'string' || comp.child.length === 0) {
+            errors.push(
+              `Button (id=${comp.id}) MUST carry a non-empty "child" component id.`,
+            );
+          }
+          if ('label' in (comp as Record<string, unknown>)) {
+            errors.push(
+              `Button (id=${comp.id}) MUST NOT include "label"; use a child Text component.`,
+            );
+          }
+        }
🤖 Prompt for 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.

In `@packages/genui/server/agent/a2ui-validator.ts` around lines 282 - 289, The
validator currently only enforces hasActionName for components in
requiresAction; extend that logic to fully validate Button payloads: when
comp.component === 'Button' (and when requiresAction.has(comp.component)
applies), check comp.action as before (using hasActionName) AND verify the
Button-specific schema—ensure a child id is present (e.g., comp.child or
comp.childId) and reject/emit an error if an inline label is present (e.g.,
comp.props?.label) since label is deprecated; push clear messages onto errors
for missing child id and for deprecated inline label using the same errors array
and context (comp.component, comp.id).
🤖 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 `@packages/genui/a2ui-playground/src/pages/AIChatPage.css`:
- Around line 128-132: Rename the `@keyframes` identifier chatMessageActionSpin to
kebab-case (e.g., chat-message-action-spin) and update every usage of that
keyframe (animation, animation-name, and any JS/React references that inject or
reference the animation) to the new name; ensure you update symbols in CSS
classes/selectors where animation: chatMessageActionSpin ... or animation-name:
chatMessageActionSpin appears so stylelint no longer reports the kebab-case
violation.

In `@packages/genui/a2ui-playground/src/pages/AIChatPage.tsx`:
- Around line 130-135: The getA2UIActionStreamEndpoint function currently relies
on a regex replace that will silently return the original URL if
getA2UIChatEndpoint() doesn't end with "/a2ui/stream"; update
getA2UIActionStreamEndpoint to validate the returned chat endpoint (call
getA2UIChatEndpoint())—if it matches the expected suffix, perform the
replacement to "/a2ui/action/stream", otherwise either construct the action
stream endpoint deterministically (e.g., by ensuring the base path and appending
"/a2ui/action/stream") or throw/log a clear error; reference
getA2UIActionStreamEndpoint and getA2UIChatEndpoint when locating the code and
add a defensive check and explicit handling for non-matching inputs.
- Around line 649-853: The action streaming flow in handleMessage lacks
cancellation so in-flight fetches can update state after unmount or when new
actions arrive; add an AbortController ref (e.g., actionAbortRef via
useRef<AbortController | null>) and before starting a new action abort any
existing controller, pass controller.signal to window.fetch (and into
readA2UIResponse if it accepts a signal), check signal.aborted inside streaming
callbacks before calling setMessages or posting to previewFrameRef, and in the
effect cleanup abort the current controller; ensure you clear actionAbortRef
when the request finishes or fails so subsequent actions create fresh
controllers.

In `@packages/genui/server/app/a2ui/action/stream/route.ts`:
- Around line 72-80: Change the two validation early-returns that check
body.threadId and body.action.name so they return a 400 Bad Request instead of
the default 200; specifically update the handlers around the checks for "if
(!body || !body.threadId)" and "if (!body.action || !body.action.name)" to call
jsonWithCors (or set the response status) with HTTP status 400 and the same
error payload (e.g., { ok: false, error: '...' }) so clients receive a proper
400 response.

---

Outside diff comments:
In `@packages/genui/server/agent/a2ui-validator.ts`:
- Around line 282-289: The validator currently only enforces hasActionName for
components in requiresAction; extend that logic to fully validate Button
payloads: when comp.component === 'Button' (and when
requiresAction.has(comp.component) applies), check comp.action as before (using
hasActionName) AND verify the Button-specific schema—ensure a child id is
present (e.g., comp.child or comp.childId) and reject/emit an error if an inline
label is present (e.g., comp.props?.label) since label is deprecated; push clear
messages onto errors for missing child id and for deprecated inline label using
the same errors array and context (comp.component, comp.id).
🪄 Autofix (Beta)

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 47bef139-0666-4a6d-b2ee-36253627e7e4

📥 Commits

Reviewing files that changed from the base of the PR and between 8aebe79 and c04ed04.

📒 Files selected for processing (11)
  • packages/genui/a2ui-playground/lynx-src/a2ui/App.tsx
  • packages/genui/a2ui-playground/src/pages/AIChatPage.css
  • packages/genui/a2ui-playground/src/pages/AIChatPage.tsx
  • packages/genui/a2ui-playground/src/render.tsx
  • packages/genui/a2ui-playground/src/utils/renderUrl.ts
  • packages/genui/server/agent/a2ui-catalog.ts
  • packages/genui/server/agent/a2ui-prompt.ts
  • packages/genui/server/agent/a2ui-validator.ts
  • packages/genui/server/app/a2ui/action/stream/route.ts
  • packages/genui/server/app/a2ui/stream/route.ts
  • packages/genui/server/service/a2ui-agent.ts

Comment thread packages/genui/a2ui-playground/src/pages/AIChatPage.css Outdated
Comment thread packages/genui/a2ui-playground/src/pages/AIChatPage.tsx
Comment thread packages/genui/a2ui-playground/src/pages/AIChatPage.tsx
Comment thread packages/genui/server/app/a2ui/action/stream/route.ts
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented May 19, 2026

Merging this PR will not alter performance

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 2 improved benchmarks
❌ 1 regressed benchmark
✅ 78 untouched benchmarks
⏩ 26 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
002-hello-reactLynx-destroyBackground 669.9 µs 916.7 µs -26.93%
transform 1000 view elements 45.9 ms 41.9 ms +9.66%
basic-performance-large-css 19 ms 16.1 ms +17.48%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing Sherry-hue:feat/chat-action (eddbe61) with main (8aebe79)

Open in CodSpeed

Footnotes

  1. 26 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@relativeci
Copy link
Copy Markdown

relativeci Bot commented May 19, 2026

Web Explorer

#10034 Bundle Size — 903.49KiB (~-0.01%).

eddbe61(current) vs 531ef76 main#10033(baseline)

Bundle metrics  Change 2 changes
                 Current
#10034
     Baseline
#10033
No change  Initial JS 45.06KiB 45.06KiB
No change  Initial CSS 2.22KiB 2.22KiB
Change  Cache Invalidation 8.34% 8.33%
No change  Chunks 9 9
No change  Assets 11 11
Change  Modules 232(+0.87%) 230
No change  Duplicate Modules 11 11
No change  Duplicate Code 27.12% 27.12%
No change  Packages 10 10
No change  Duplicate Packages 0 0
Bundle size by type  Change 1 change Improvement 1 improvement
                 Current
#10034
     Baseline
#10033
Improvement  JS 499.11KiB (~-0.01%) 499.15KiB
No change  Other 402.16KiB 402.16KiB
No change  CSS 2.22KiB 2.22KiB

Bundle analysis reportBranch Sherry-hue:feat/chat-actionProject dashboard


Generated by RelativeCIDocumentationReport issue

@relativeci
Copy link
Copy Markdown

relativeci Bot commented May 19, 2026

React Example

#8460 Bundle Size — 237.24KiB (0%).

eddbe61(current) vs 531ef76 main#8459(baseline)

Bundle metrics  no changes
                 Current
#8460
     Baseline
#8459
No change  Initial JS 0B 0B
No change  Initial CSS 0B 0B
No change  Cache Invalidation 0% 0%
No change  Chunks 0 0
No change  Assets 4 4
No change  Modules 198 198
No change  Duplicate Modules 80 80
No change  Duplicate Code 44.74% 44.74%
No change  Packages 2 2
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#8460
     Baseline
#8459
No change  IMG 145.76KiB 145.76KiB
No change  Other 91.48KiB 91.48KiB

Bundle analysis reportBranch Sherry-hue:feat/chat-actionProject dashboard


Generated by RelativeCIDocumentationReport issue

@relativeci
Copy link
Copy Markdown

relativeci Bot commented May 19, 2026

React Example with Element Template

#729 Bundle Size — 200.08KiB (0%).

eddbe61(current) vs 531ef76 main#728(baseline)

Bundle metrics  Change 2 changes
                 Current
#729
     Baseline
#728
No change  Initial JS 0B 0B
No change  Initial CSS 0B 0B
No change  Cache Invalidation 0% 0%
No change  Chunks 0 0
No change  Assets 4 4
Change  Modules 89(-3.26%) 92
No change  Duplicate Modules 27 27
Change  Duplicate Code 39.81%(+0.13%) 39.76%
No change  Packages 2 2
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#729
     Baseline
#728
No change  IMG 145.76KiB 145.76KiB
No change  Other 54.32KiB 54.32KiB

Bundle analysis reportBranch Sherry-hue:feat/chat-actionProject dashboard


Generated by RelativeCIDocumentationReport issue

@relativeci
Copy link
Copy Markdown

relativeci Bot commented May 19, 2026

React MTF Example

#1593 Bundle Size — 208.18KiB (0%).

eddbe61(current) vs 531ef76 main#1592(baseline)

Bundle metrics  no changes
                 Current
#1593
     Baseline
#1592
No change  Initial JS 0B 0B
No change  Initial CSS 0B 0B
No change  Cache Invalidation 0% 0%
No change  Chunks 0 0
No change  Assets 3 3
No change  Modules 193 193
No change  Duplicate Modules 77 77
No change  Duplicate Code 44.24% 44.24%
No change  Packages 2 2
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#1593
     Baseline
#1592
No change  IMG 111.23KiB 111.23KiB
No change  Other 96.95KiB 96.95KiB

Bundle analysis reportBranch Sherry-hue:feat/chat-actionProject dashboard


Generated by RelativeCIDocumentationReport issue

@relativeci
Copy link
Copy Markdown

relativeci Bot commented May 19, 2026

React External

#1575 Bundle Size — 695.64KiB (0%).

eddbe61(current) vs 531ef76 main#1574(baseline)

Bundle metrics  no changes
                 Current
#1575
     Baseline
#1574
No change  Initial JS 0B 0B
No change  Initial CSS 0B 0B
No change  Cache Invalidation 0% 0%
No change  Chunks 0 0
No change  Assets 3 3
No change  Modules 17 17
No change  Duplicate Modules 5 5
No change  Duplicate Code 8.59% 8.59%
No change  Packages 0 0
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#1575
     Baseline
#1574
No change  Other 695.64KiB 695.64KiB

Bundle analysis reportBranch Sherry-hue:feat/chat-actionProject dashboard


Generated by RelativeCIDocumentationReport issue

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@packages/genui/server/agent/a2ui-prompt.ts`:
- Around line 149-152: The few-shot example is embedding a secret ("password")
into action.event.context which can leak plaintext credentials; update the
example in a2ui-prompt.ts to remove or replace the "password" field with a
non-secret placeholder (e.g., "userId" or "demo_password_placeholder") and add a
short inline comment or a bullet in the surrounding prompt text enforcing a hard
rule: never include secrets (passwords/tokens/keys) in action.event.context.
Locate the example object containing "context" (the example action/event
payload) and change the key/value accordingly and add the explicit rule near
that example so future examples follow the non-secret pattern.
🪄 Autofix (Beta)

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ffd6b5be-3b0f-47d0-aa2c-18070b31e8c9

📥 Commits

Reviewing files that changed from the base of the PR and between c04ed04 and eddbe61.

📒 Files selected for processing (11)
  • packages/genui/a2ui-playground/lynx-src/a2ui/App.tsx
  • packages/genui/a2ui-playground/src/pages/AIChatPage.css
  • packages/genui/a2ui-playground/src/pages/AIChatPage.tsx
  • packages/genui/a2ui-playground/src/render.tsx
  • packages/genui/a2ui-playground/src/utils/renderUrl.ts
  • packages/genui/server/agent/a2ui-catalog.ts
  • packages/genui/server/agent/a2ui-prompt.ts
  • packages/genui/server/agent/a2ui-validator.ts
  • packages/genui/server/app/a2ui/action/stream/route.ts
  • packages/genui/server/app/a2ui/stream/route.ts
  • packages/genui/server/service/a2ui-agent.ts
🚧 Files skipped from review as they are similar to previous changes (9)
  • packages/genui/server/service/a2ui-agent.ts
  • packages/genui/a2ui-playground/src/utils/renderUrl.ts
  • packages/genui/server/agent/a2ui-validator.ts
  • packages/genui/a2ui-playground/lynx-src/a2ui/App.tsx
  • packages/genui/server/app/a2ui/stream/route.ts
  • packages/genui/a2ui-playground/src/render.tsx
  • packages/genui/server/agent/a2ui-catalog.ts
  • packages/genui/a2ui-playground/src/pages/AIChatPage.tsx
  • packages/genui/server/app/a2ui/action/stream/route.ts

Comment thread packages/genui/server/agent/a2ui-prompt.ts
@Sherry-hue Sherry-hue enabled auto-merge (squash) May 19, 2026 07:31
@Sherry-hue Sherry-hue merged commit 9ac6751 into lynx-family:main May 19, 2026
64 of 67 checks passed
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