feat(chat): open agents' plans, to-dos and edits in a Work panel - #181
Merged
Merged
Conversation
What an agent hands the person to look at - a plan to approve, a to-do list, an edit - reached the chat as 400 characters of JSON behind a collapsed tool row. Each tool call's own input is now read in main into a bounded SessionMessage.artifact, for Claude (ExitPlanMode, TodoWrite, TaskCreate/TaskUpdate, Edit, MultiEdit, Write), Codex (update_plan, apply_patch, the FileChange items code-mode patches complete with, the exec stream's todo_list and file_change) and Copilot (exit_plan_mode, edit, create, apply_patch), plus ACP plan updates and diff content. The renderer folds them into a Work panel beside the transcript: Plan (every version, flagged while it waits for approval), To-dos (where the list stands now) and Edits (per file, each change as its call described it, with a link to Changes for what is on disk). A row that carries one opens the panel at itself in one click; the header's Work key and cmd+J open it on what matters now. Under 720px of deck the panel covers the conversation instead. A plan gate's approval card now shows the plan. A call whose result failed is marked on its own row, matched by tool-use id, so an edit that never landed reads as one. Codex's streamed file_change rows are named as their logged FileChange rows are, so a rejoined turn matches the two.
Code Coverage OverviewLanguages: TypeScript TypeScript / code-coverage/unitThe overall line coverage in commit a751c3b in the Show a line coverage summary of the most impacted files.
TypeScript / code-coverage/componentThe overall line coverage in commit a751c3b in the Show a line coverage summary of the most impacted files.
Updated |
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.
Summary
Agents hand the person things to look at while they work: a plan to approve, a to-do list, edits to files. Until now each of these reached the chat as up to 400 characters of JSON behind a collapsed tool row. The plan gate's Approve the plan card didn't even show the plan.
This change carries them as structured data and opens them in a Work panel beside the conversation:
A tool row that carries one of these is a single button: it shows the plan's title, the list's progress, or the files with their +/− counts, and one click opens the panel at that item. The header's Work key (⌘J) appears once the conversation holds any of them and opens on what matters now. Escape closes the panel and returns focus. The approval card now shows the plan above its two answers.
What each agent contributes
ExitPlanModeTaskCreate/TaskUpdate(numbered by their results),TodoWriteEdit,MultiEdit,Writeupdate_plan, the exec stream'stodo_listapply_patch(function, freeform, or as a shell command), the typedFileChangeitems that code-mode patches complete with, the exec stream'sfile_change(paths only)exit_plan_modeedit,create,apply_patchplanupdatesdiffcontent on the announcing callHow
src/main/parsers/artifacts.ts(IO-free) reads each call's input intoSessionMessage.artifact/ChatEvent.artifact. It is shared by the three log parsers and by the stream parsers inchat.tsandacp-core.ts. Everything is bounded in main (24 files per call, 400 lines per file, 400 characters per line, 3 lines of context around each change of a before/after pair) because it crosses IPC on every transcript read.failed), matched to its result by tool-use id (Claude) ortoolCallId(Copilot). Pairing by adjacency got parallel calls wrong.execscripts, so their only record of a change is theFileChangeitem. Those are now rendered asapply_patchrows, unless the rollout applies patches as calls of their own (then the call is the row, never both). Streamedfile_changerows use the same name, so a rejoined turn can match the two.work.ts); it never parses tool JSON.WorkPanel.tsxreuses the card tabs and the review's diff blocks (DiffLinesis now exported fromInstructionDiff)..chat-deck→.chat-main+ panel). Below 720px of deck, a@containerrule makes the panel cover the conversation instead, the way Changes does. Changes closes a covering panel first so it never opens unseen behind it.Screenshots (UI tour fixture world)
Edits, opened from the row (desktop):

To-dos (⌘J) · the plan in its approval card · the plan in the panel:



900×700: the panel covers the conversation:

Testing
npm run typecheckandnpm test(124 files, 1914 passed) on the branch rebased ontomainnpm run test:e2e: 46 passed (the minimum-window audit and the a11y walk included; 6 opt-in specs skipped as usual)npm run ui:tour: newchat-work-edits,chat-work-todos,chat-planandchat-work-planshots at desktop, 900×700, the 560×420 floor and 200%; the fixture world gained Claude tasks and a plan waiting for approvaltests/artifacts.test.ts(every tool shape, patch and unified-diff parsing, bounds),tests/work.test.ts(the fold), parser tests against real log shapes for all three CLIs, stream and ACP mapping, andtests/component/work-panel.test.tsxNot covered
tool_call_updatefor a call already on screen is not carried. The session's log still has it.