Repository navigation
feat(site/src/pages/AgentsPage): keep agent attachments in working blocks and list them under Attachments - #30788
Conversation
…ocks and list them under Artifacts With collapsed assistant steps, file parts the agent attaches now fold into the working block of the step that made them instead of ending the block, and rows outside any block drop them. The Summary tab gains an Artifacts section that lists assistant file parts from the loaded messages, newest first, with a button to load earlier messages.
…rtifacts section Tighten comments, require hideAttachments, drop an unreachable href filter, seed the plan story through its query key, and prefix new fixtures with mock.
…chment-only rows outside working blocks chatd commits a local tool's attachments as their own assistant message after the call. When that message falls outside every working block (for example after propose_plan), hiding its files left an empty transcript item and an extra gap. Skip such rows while steps collapse, and model the persisted shape in the grouping test and the attachment story fixtures.
… artifacts on hydration - Move the attachment-only check next to the working-block grouping as isAttachmentOnlyRow, and skip such rows with one condition in the timeline. - Add useChatMessages to the chat store and use it in ChatPageContent and ChatSummaryPanel, so every caller keeps the desync warning. - Hide the Artifacts empty copy while fetched pages are still hydrating the store, so it does not flash "No artifacts yet." on mount. - Resolve each artifact's href in getChatArtifacts (getAttachmentHref now lives in utils/chatAttachments), and give FileCard a fullWidth prop in place of the className override. - Type onFetchMoreMessages as a promise, as the page passes it, and pick the panel's paging props from ChatArtifacts.
… Attachments section The Summary panel section now reads "Attachments (N)" and collapses from its header, matching the name the transcript already uses. The count is hidden while fetched pages hydrate the store. Also applies the second frontend review round: the section picks its paging props from EarlierMessagesProps, disables history paging while messages hydrate, and Summary panel stories build a store per story.
…on steady while messages hydrate Every live durable message reaches the store before the query cache, so isHydratingMessages is true for one commit after each one. The section hid its count, empty copy, and paging button during that commit, which made them blink on every message. The panel now treats only an empty store during hydration as loading, and the paging button is disabled only while a page is loading. Also applies the third frontend review round: the header uses SectionHeader with the animated chevron, the attachment fixtures share a step helper, and stories cover the loading and live-message states.
…ts list and build attachment fixtures from tool steps Render the Attachments list, its empty text, and the paging button as three sibling branches instead of a fragment inside a ternary. Give download cards the focus ring that the sibling text preview button already has. Build attachment fixtures on workingStepMessages, so each attached file sits between its tool call and its result. The Summary panel stories now use the same builder, so their files also have tool results. Add a story for a paging retry that is in flight. Correct comments: hydration blocks only the transcript's automatic pager, and only agent attachments hide outside working blocks. Restore the splitRowBlocks doc and the NotVisible story note, and delete story comments that repeat the loading-gate comment.
…ogic into grouping helpers Move the timeline's section split and attachment filter into getRowBlocks beside splitRowBlocks, so ChatMessageItem gains a call instead of new logic. Rename isAttachmentOnlyRow to isAgentAttachmentOnlyRow, since it matches only agent rows. Expose the paging button's spinner to assistive technology, as the other load-more buttons do (InboxPopover, HistorySidebar). Name files beside sources in the splitRowBlocks doc, and move the hydration note from the shared EarlierMessagesProps field to the transcript gate that applies it. Pass attachmentStepMessages one options object and type its tool fields from the generated parts. Add stories for the focused header and download card, and for an expired attachment in the Summary panel.
…gh getRowBlocks Unexport splitRowBlocks, whose only outside caller after the previous commit was its test, and run the split tests through getRowBlocks. A new case covers hideAttachments dropping a row's files. Say in the getRowBlocks doc why it drops files, shorten the isAgentAttachmentOnlyRow and fixture docs, and name the Summary panel's paging rest props earlierMessages, as ChatMessageScroller does. Build the ExpiredAttachment story's file metadata from MockChatFileMetadata.
…ents loading gate Move the Summary panel's loading gate into getLoadedAgentAttachments beside getAgentAttachments and cover it with a unit test: only an empty store during hydration leaves the list undefined, so a live message does not blank it. Seed the new ChatSummaryPanel stories through parameters.queries instead of API spies, and build their store with createAttachmentStore. Name getRowBlocks' options type, keep the docs to what the names omit, and drop the hideAttachments prop comment that the getRowBlocks doc now covers.
…y to FileCard FileCard takes an expired prop instead of reading the FileProbe context, so the Attachments section passes expiry from chat.files and drops its second FileProbeProvider, and the transcript's AttachmentBlock reads the context it already sits in. Keep comments to what the code does not say: the getRowBlocks doc describes hideAttachments, the collapse rule moves to its derivation in ConversationTimeline, the splitRowBlocks doc is back to its original text with a one-line reason at the file check, and the getLoadedAgentAttachments, EarlierMessagesProps, and fixture docs are shorter. Delete a test comment that pinned a commit count and the NotVisible story comment that restated its assertions.
State getRowBlocks' section contract and where hidden files go, and drop the timeline comment that narrated the hideAttachments condition. Say that the Attachments list stays undefined until the first page reaches the store, that FileCard never probes expiry, and that every consumer of the paging props sees a page any of them loads.
…c to agent rows User rows render their uploads from the message display state, so only the files hidden from agent rows show up under Attachments.
|
@codex review
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 30007ac6a4
ℹ️ About Codex in GitHub
Your team has set up Codex to 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 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
@codex review
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 30007ac6a4
ℹ️ About Codex in GitHub
Your team has set up Codex to 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 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 30007ac6a4
ℹ️ About Codex in GitHub
Your team has set up Codex to 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 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review
|
|
Codex Review: Didn't find any major issues. Hooray! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
In
/agentschats with Collapse assistant steps on, files the agent attaches (attach_file, computer-use screenshots, thepropose_planplan file) rendered as the answer after a Working block: the block closed at the attachment, and a turn that kept working split into several blocks. They now fold into the Working block of the step that made them and show when it is expanded. Rows outside every block (an uncollapsiblepropose_planorask_user_questionrow, a parked running tool) hide their files, and an agent row left with nothing else is not rendered. With the preference off, the transcript is unchanged, except that download cards get the focus ring the text-preview button beside them already has.To keep those files reachable, the right panel's Summary tab gets a collapsible Attachments (N) section, open by default. It lists the files the agent attached in the loaded messages, newest first, as the transcript's download cards (expired tile included); user uploads stay in their messages. While older history is unloaded, the section says so and offers Load earlier messages. Until the first message page reaches the chat store, the header shows no count, so a chat with files never flashes "(0)", and live messages do not make the list blink.
Frontend only: production code changes 366 lines; the rest is tests, stories, and fixtures. This overlaps #30656, which renders attached files below the Working block; this PR keeps them inside the block, as the newer request asks, so whichever lands second reconciles
workingBlockGrouping.tsandConversationTimeline.tsx.Collapse on. The turn's files stay in the collapsed Working block, and the Summary panel lists them under Attachments (3), newest first.
Block expanded. Each file follows the step that attached it.
Expired file. After
old.zipexpires, the panel shows the expired tile in its place.Follow-ups, out of scope: split
ConversationTimeline.tsx(795 lines before, 805 now); keep focus on load-more buttons while they fetch (HistorySidebar,InboxPopover, this one); show the fetch error in both message pagers; share probe-learned expiry between the transcript and the panel; flag evicted files newer than the newest linked one, such as an evicted newest file (deriveEvictedFileIdscannot yet tell them from files the chat has not linked yet); name the file on the expired tile.Testing
pnpm lint, the AgentsPage unit tests (122 files, 2133 tests), and the story tests for the touched area (13 files, 261 tests) pass on 30007ac.isAgentAttachmentOnlyRow,getAgentAttachments(agent files only, newest first), the loading gate (a live message does not blank the list), and the load button. Each fails when its behavior breaks.ChatAttachments(12 states), fourChatSummaryPanelstates, and three working-block states.Review record
coder-frontend-review(8 global roles),frontend-deep-review(8 project roles), andfrontend-review(FE1 to FE10), read-only. Round 9 ran all 17 roles on b53897c; rounds 10 and 11 re-checked the comment-only fixes on 3e47d30 and 30007ac.ConversationTimeline.tsxsplit follow-up listed above.ChatAttachmentsdoc. Round 4 found no major issues.Justified FAILs:
InboxPopover,HistorySidebar); follow-up for all three.Dropped findings (finding: reason):
ConversationTimeline.tsxnow: the PR adds call sites, a prop, and two flags; follow-up, and fix(site): split Working blocks at assistant text and scroll to their end on expand #30656 edits it.SidebarSectionHeader: a sidebar h2 with no aria-controls; the section uses the Radix Collapsible inSectionHeader.isFileRenderBlock: inlineblock.typechecks are the grouping module's idiom.everyhelper: a one-line idiom with different predicates in two modules.getMemberRow: two conditions, and fix(site): split Working blocks at assistant text and scroll to their end on expand #30656 rewritesgetMemberRow.OmitoverPick: thePickreuses the exportedEarlierMessagesPropstypes.AgentChatPageViewpasses every prop explicitly.isAgentAttachmentOnlyRow: it takes the grouping module's row type and sits besidegetRowBlocks.{ message }wrapper: retyping moves the map intoConversationTimeline.tsx, which fix(site): split Working blocks at assistant text and scroll to their end on expand #30656 edits.cvaforfullWidth: one boolean in a pre-existing component.typeimports: the repo's style for mixed imports.ChatStoreHandle:ChatStoreis the exported type; the handle is a file-local alias.FileCardProps: the file types its components inline.GetRowBlocksOptions: named likeGroupWorkingBlocksOptions.AgentAttachmentbesidereadAgentAttachmentText: that one reads user uploads, in another module.SessionTimeline.tsx) and the transcript's retry label.getAgentAttachmentsdoc: it states the ordering and the upload exclusion.isHydratingMessagescomment: pre-existing and consistent with its source.EarlierMessagesPropsdoc: the doc states the contract.splitRowBlocksdoc: the base text stays; a comment at the file condition gives the reason.NotVisiblecomment: this PR made it false.NotVisibleassertions: pre-existing.hasMoreMessages: tsc enforces the forward;WithEarlierMessagesis the visual.getRowBlockscall.ChatErrorstory: the grouping test fails on the same break; no new state.useChatMessagesunit test: a moved derivation with no new logic.LoadingMessagesandLiveMessageArriving: each catches a call-site break (test audit).ChatPageContent.storiesdoes.NotVisiblecapture shows the section: it renders whateverisVisibleis..mapcallback: a single-use wrapper (site/AGENTS.md).useChatMessagesas a function: it owns two subscriptions that three components share.useFileProbes()before the branches: hooks cannot be conditional, and the default context is safe.chat.files, polled every 5 s while the chat runs.deriveEvictedFileIdsgap; follow-up.void onFetchMoreMessages():fetchNextPagereports errors in its result, and the transcript calls it the same way.ChatSectionHeaderdoes.messageHelpers.tsimportschatAttachments.ts: the page already loads it; dpdm finds no cycle.m-0on the empty state:index.csssets paragraph margins.UAT observations, not failures: