fix(web): render agent-authored HTML views in chat again - #152
Merged
Conversation
A t3_show_ui call stored its document server-side and left an ordinary collapsed tool row in the transcript, rendering nothing. The renders were stored correctly and the handle reached the timeline; the client refused to mount them. PR #145 disabled agent views behind a blanket AGENT_UI_SURFACES_RUNTIME_ENABLED = false. The concern behind it was real but URL-only: a framed collaboration app can decline to join the room named by the URL and render unrelated origin-local state instead. The flag also took out agent-authored HTML, which has none of that problem — the document is what the agent produced, and it mounts from srcDoc in an unmodified allow-scripts sandbox with an opaque origin. AgentUiRenderFrame already blocked URL renders on their own, so the global flag was both over-broad and redundant, and it left the Experiments toggle permanently disabled. Narrow the gate to the kind it was justified for: isAgentUiSurfaceRenderable() allows html and blocks url. A url handle still leaves the ordinary tool row, and the frame keeps its URL-blocked message for a stored render whose kind disagrees with the record. Re-enables the Experiments toggle with a truthful description. Adds the regression guard that was missing: an HTML surface mounts an iframe carrying sandbox="allow-scripts" and the agent's markup in srcdoc. Claude Opus 5 via T3 Code.
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: unavailable · PR result: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A
t3_show_uicall left an ordinary collapsed tool row in the transcript and rendered nothing. Reported against threadbc541f47-d2d9-4a9d-91ba-524fe4c86ca9, where an agent sent 6 HTML views twice — the second time with all content statically baked in, on the theory that scripts were being blocked. Neither batch appeared.The server side was never at fault. All 12 renders are stored correctly, and the render handle reaches the timeline row:
The client refused to mount them. #145 disabled agent views behind a blanket kill switch:
AgentUiSurfaceRowshort-circuits on it and returns the plain tool row. bkt3 is running0436d9d6, which contains it.Fix
The concern behind #145 was real but URL-only: a framed collaboration app can decline to join the room named by the URL and render unrelated origin-local state instead — a box that looks right and shows the wrong thing. The flag also took out agent-authored HTML, which has none of that problem. The document is what the agent produced, and it mounts from
srcDocin an unmodifiedsandbox="allow-scripts"iframe, so it has an opaque origin and can reach neither T3 nor the network as the signed-in user.AgentUiRenderFramealready blocked URL renders independently, so the global flag was over-broad and redundant — and it left the Experiments toggle permanentlydisabled.This narrows the gate to the kind it was justified for.
isAgentUiSurfaceRenderable()allowshtml, blocksurl. Aurlhandle still leaves the ordinary tool row, and the frame keeps its URL-blocked message for a stored render whose kind disagrees with the record.Because the renders are already in the database, the views in the reported thread appear retroactively once this deploys — nothing needs re-sending.
Evidence
Before: in the reported thread every
t3_show_uicall renders as a bare collapsed tool row — the wrench icon, the truncated JSON argument, and a chevron — with no view box beneath it. (Screenshot is in the originating T3 session; this agent has no way to upload an attachment.)I have not verified this in a browser; the "after" is covered by tests, not a screenshot. New regression guard asserts an HTML surface mounts an iframe carrying
sandbox="allow-scripts"and the agent's markup insrcdoc— the coverage whose absence let #145 disable the working half of the feature silently.vp test run apps/web/src/fork/ MessagesTimeline.agentUi.logic.test.ts ActivityPayloadProjection.agentUi.test.ts— 59 passed / 10 filesvp run typecheck(@t3tools/web) — clean0436d9d6, the SHA bkt3 currently runs, and re-verified thereNote on the base
This targets
bkmaindirectly at the maintainer's explicit request, skipping the usual expbkt3 soak. All touched source files are byte-identical betweenbkmainandexpbkmain, so the change is isolated from the 9 mobile commits currently staged inexpbkmain.Claude Opus 5 via T3 Code.
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.