Skip to content

implement keep/undo in copilot harness - #342469

Merged
Justin Chen (justschen) merged 25 commits into
mainfrom
agents/keep-undo-flow-research-prototype
Oct 21, 2026
Merged

Justin Chen (justschen) merged 25 commits into
mainfrom
agents/keep-undo-flow-research-prototype

Conversation

@justschen

@justschen Justin Chen (justschen) commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator

What this does

This PR adopts the merged optional nonce contract from microsoft/agent-host-protocol#539 and optional ChangesetFile.isEditing field from microsoft/agent-host-protocol#540, with the vendored protocol synced to merge commit 1d639930. No negotiated protocol-version or Copilot SDK bump is required.

Agent Host chats lost the local harness's per-change review when their editing session was reduced to checkpoints (fe781b7, 5bb2223). Since then, the agent host gained its own review state: a Working Set changeset with the tool edits the user hasn't kept, whose accept-changes and discard-changes operations keep or undo a file or a single hunk (#342812, #342614, #342841, #342899). The working set above the chat input uses them. This PR shows the same Working Set in the editor with the local harness's Keep/Undo UX, using optional AHP file/operation nonces and per-file editing state rather than private metadata, with no new SDK surface:

  1. Getting the edits. A new AgentHostEditReview subscribes to the chat session's Working Set and reads each file's baseline and the content the host last recorded.
  2. Building the review. It creates one entry per file, using the same ChatEditingModifiedDocumentEntry local chat uses. The original side is the file's Working Set baseline with independent user edits incorporated, and the modified side is the live document. Clean documents reload the agent's recorded write; dirty documents reconcile non-conflicting disk edits with their unsaved text before the review updates.
  3. Showing it. AgentHostSnapshotController, the chat's IChatEditingSession, used to be always empty and now exposes those entries. The existing UI reads from it unchanged: the editor overlay (Keep / Undo / n of m), hunk Keep/Undo, keybindings, the legacy working set and the "Suggested Edits" multi-diff.
  4. Keeping and undoing. File Keep and Undo invoke accept-changes and discard-changes on the file, and hunk Keep and Undo invoke them on the hunk's lines. The host records the decision, and the editor and the working set both follow its updated Working Set.

The scope is the editor-to-Working-Set bridge, the host coordination needed for review during a turn, and synchronization of incoming agent edits with dirty documents. It reuses the existing editor UI and host review operations rather than introducing a second review ledger or replacing the SDK's edit tools.

Behavior:

  • One source of truth: the editor and the working set show the same Working Set, so Keep or Undo in either one shows in the other. Review state lives in the agent host, not in the window.
  • Timing: the host updates the Working Set after each of the agent's edits, so the editor highlights a change as soon as the host records it.
  • During a turn, Keep and Undo work between edits, except in a file the agent is writing: that file is read-only, its overlay shows progress, and its Keep and Undo stay disabled until its edit is recorded and the editor has synchronized and diffed it. Other files remain reviewable. See Keep and Undo while the agent works.
  • Keep marks the changes as kept with the host and leaves the file unchanged. Undo reverts them on disk, and undoing a created file deletes it. Either way, the host only acts on changes the user hasn't kept.
  • User edits: typing outside the agent's changes is moved into the editor's baseline, so it isn't shown as the agent's, including before the first editor diff finishes. Incoming non-conflicting agent edits are combined with dirty text without implicitly saving it. Keep leaves that text unsaved. Undo retains its save-first behavior, without save participants, because the host reverts on disk with a 3-way merge; it stops if saving fails and refuses to revert lines the user changed too.
  • Safety: Keep and Undo capture the displayed Working Set file's optional nonce before waiting for earlier operations or updates, and echo it on the operation target. A stale supplied nonce fails with the standard AHP Conflict error instead of deciding on newer changes. Deferred editor-state transitions are bound to that revision too: a response for an older revision cannot mark newer changes accepted/rejected or exclude them from Keep/Undo All. Omission remains compatible with older clients and hosts; ordinary operation/write-safety checks still apply. Hunk operations fail closed if their lines cannot be mapped. Overlapping dirty edits preserve the user's text, warn, and suspend that file's editor review until its contents can be reconciled.
  • Accessibility and recovery: successful file and hunk decisions play the existing edits-kept/edits-undone signals without duplicate success signals. Existing editor and working-set accessibility guidance is reused without a second implementation-specific explanation. Failed synchronization releases the editor's review lock, reports how to recover, and retries blocked files after save/revert. If closing a review diff is cancelled, its original stays alive until the editor actually closes, even after the review is disposed; retained entries no longer keep editing restrictions active.
  • Scope: chat.editor.keepUndo.enabled (experimental, default off, read-only off in the Agents window). Chats in the chat view or an editor can review any provider/connection that advertises a Working Set; there is no Copilot-only or local-host-only gate. Existing connection URI mapping keeps remote files and operations on their owning host. Inline chat keeps its own review, and subagent chats are identified from their advertised catalog origin rather than URI structure.

Working set

This PR does not redesign the working set above the chat input. Its earlier Session Changes | To Review tabs are gone: main's working set already lists the Working Set with Keep and Undo, and the editor now shows the same changes. Working-set actions retain and echo the nonce associated with the displayed diff, never substituting a newer nonce while an operation or confirmation is pending.

Session Changes now reflect Undo

Earlier versions of this PR reverted files from the workbench, and the host didn't record that, so Session Changes kept showing undone edits. Undo now goes through the Working Set's discard-changes, which the host records as an edit. Session Changes, This Turn and the working set update after an Undo, and the agent host stays the only place that decides what was kept. Uncommitted and Branch changes read git and update as before.

Keep and Undo while the agent works

As in local chat, changes can be kept or undone while the agent works, except in a file the agent is writing. The host side:

  • Live Working Set: the host refreshes the Working Set after each of the agent's edits instead of 5 seconds after they pause, so the editor highlights each change as soon as it's recorded.
  • Nonces: each Working Set file publishes an opaque ChangesetFile.nonce; Keep and Undo echo it in ChangesetOperationTarget.nonce. AHP recommends updating published nonces when file contents change; VS Code derives its nonce from the latest recorded edit, accept, or discard. This host checks supplied nonces against the authoritative recorded change, including revalidation at the mutation boundary, and rejects stale ones with Conflict (-32011). The private vscode.workingSetVersion metadata and active-turn missing-version rejection are removed. Clients may omit nonces, including during a turn, subject to the existing operation/provider safety gates. No capability or protocol-version negotiation is added.
  • Keep stays available while a turn runs, since it only records which changes were kept.
  • Undo stays available while a turn runs when the provider's edit tool calls wait for it. Copilot's do, through the new IAgentHostFileEditCoordinator and shared FileEditTracker: onPreToolUse waits for an Undo of the file in progress, and an Undo waits for the edits of its file in progress, so each edit and each Undo is recorded in order. The tracker also publishes Claude's observed editing state, but does not claim that Claude's message stream can pause tool execution. Codex retains its native turn-diff pipeline. Host-advertised operation availability remains authoritative for every provider.
  • Editing state: while an edit tool call writes a file, its Working Set entry publishes the standard AHP ChangesetFile.isEditing flag. The host clears it with a freshly computed Working Set, not an older in-flight result. The editor applies the flag even while the Working Set is recomputing and even without a chat response, and keeps the file read-only and Keep/Undo disabled until its reload or dirty-buffer reconciliation and diff finish. A newer update cancels stale reconciliation work; an earlier update cannot unlock a later edit. The field is optional and advisory: false or omission clears the indication but is not a guarantee that modifying the file is safe; host-side coordination and operation checks remain authoritative. Both private Working Set metadata helpers are removed.
  • Edit lifetime: the shared tracker owns each edit's lifetime through snapshot persistence, independently of root/subagent turn bookkeeping. There are no additional turn generations or idle-time lease sweeps. SDK hooks may continue after the root turn ends; actual tool completion or tracker disposal releases their ownership. Late completed edits are recorded under their originating turn even when their presentation is suppressed, without publishing a completion into a replacement turn.
  • Telling the agent: after an Undo, the agent gets one note with its next tool result or prompt (additionalContext), naming the file and lines and asking it not to make those changes again unless asked. Following the prompt-text rules in the agent host's AGENTS.md, it's per-turn text, byte-stable apart from the path and lines.

The editor synchronizes a document before comparing it with the Working Set and attributes the incorporated write to the agent. Without that ordering, a Working Set update could arrive before the document reload, and the later reload was mistaken for user typing, leaving no highlighted changes. Clean files still reload from disk; dirty files use the explicit reconciliation path below.

User edits and dirty files

Local's model-based tools edit the same buffer as the user. The Copilot SDK instead writes to disk, so a dirty editor can otherwise miss the agent's edit and produce a "file is newer" warning on Save. This PR reconciles those recorded disk edits into the editor; it does not redirect SDK writes or automatically save before a tool.

  1. Separate synchronization baseline. TextFileEditorModel retains immutable text from its last synchronized disk version, including successful saves, only while an explicit consumer needs it. Opted-in session handlers enable retention before the first review, and active reviews hold their own references; the final release drops the shared saved-text copies. This is not the Working Set baseline, which can span several agent edits and Keep decisions. A document that becomes dirty while retention is enabled can use its known ancestor even before its first reviewed edit. Enabling retention after a document is already dirty does not invent an ancestor.
  2. Three-way reconciliation. The model compares that ancestor with the current buffer and the recorded disk content using the editor worker. Non-conflicting incoming changes are rebased over unsaved user edits and applied through normal text-model edit operations, preserving the undo history and current line endings.
  3. Version-safe save bookkeeping. The initial disk read must match the host's recorded content. Before applying the result, the model rechecks its version, cancellation, save state, encoding, and the disk ETag/permissions with a stat rather than a second full content read. Save metadata still advances only to the incorporated read, never to a newer stat; the buffer remains dirty if it still differs from disk. The next normal Save can preserve both edits, while later unseen disk writes still receive the normal conflict protection.
  4. No silent conflict resolution. Overlaps, missing safe ancestors, timed-out comparisons, or repeated stale reads preserve the user's buffer and produce a warning. The stale editor review is removed and retried after a save/revert or a later Working Set update; the SDK's recorded changes remain in the host Working Set.
  5. Preserved editor semantics. Immutable ancestors survive LF/CRLF changes without absorbing later unsaved typing. A no-op reconciliation preserves the saved undo-version marker; when disk really changes, undoing to the old disk text must not incorrectly mark the document saved. UTF-8 BOM handling and existing encoding restrictions remain in place.

For example, if the user changes a comment without saving and the agent changes an unrelated function, the editor shows both changes, highlights the agent's function change with Keep/Undo, and keeps the user's comment unsaved. Keep does not save it. Undo still saves first before asking the host to discard the agent's change.

Only the opt-in Agent Host editor review invokes automatic reconciliation. Normal dirty-file reload behavior is unchanged. Existing send-time saving is also unchanged, and the SDK still does not automatically read unsaved editor text when deciding what to edit.

Research findings

  • The host owns Keep and Undo. The Working Set (changeset/working-set) offers accept-changes and discard-changes on a file (Resource) or on zero-based lines (Range), without confirmation. A Range target picks the hunks it touches. A discard reverts the changes since the last Keep with a 3-way merge and records the revert. Before this PR's in-turn changes, the host disabled both while a turn ran and updated the Working Set 5 seconds after edits paused.
  • It's built from the SDK hooks. CopilotAgentSession drives FileEditTracker from onPreToolUse / onPostToolUse. The Working Set is derived from those records rather than git; Session Changes and This Turn retain their existing host aggregation. The success hook provides completed-edit snapshots; the failure hook can still deliver pending Undo context. The hooks are async, so the host can hold an edit tool call back, which Undo during a turn relies on.
  • Editor hunks match host hunks. The host diffs with the editor's options (ignoreTrimWhitespace: false, no moves). Its "after" side is the content it last recorded, not the live document, so the editor maps a hunk's lines across typing the host hasn't recorded.
  • Working Set content URIs change with the content, because they're keyed by edit records. The editor compares them to tell which files changed.
  • A bug that only the live run found: ChatServiceImpl._startSession creates a chat's editing session before it registers the chat model, so the provider couldn't look the chat up and never attached the review. IChatEditingSessionProvider.createEditingSession now receives isGlobalEditingSession.
  • A race before the first editor diff: typing in a reviewed file could be applied to the original at offsets that didn't match. The Agent Host review now prepares its baseline and edit mapping through the editor worker before exposing entries, rechecking document versions and cancellation after asynchronous work. Hunk mapping also uses the worker and rejects stale or timed-out results. The shared local-chat mapping behavior is unchanged; the review supplies its mapping through an opt-in hook, with a conservative fallback if its initial diff times out.
  • Dirty reconciliation needs file-model ownership. Updating the buffer alone leaves stale save metadata, while only refreshing its ETag can hide unseen disk changes. The file model owns both reconciliation and save bookkeeping. Its disk ancestor is an immutable string rather than a deferred snapshot backed by a mutable piece tree.
  • Independent reviews: Claude Opus 5.5, GPT 6 Astra, Gemini 4 Argon and GPT 6.1 Sol each reviewed the first prototype's research and plan. That prototype tracked edits in the window; this one replaces it with the host's Working Set. Inline chat remains excluded and unreadable content fails closed. The earlier remote-host restriction has been removed in favor of advertised Working Set support and the existing connection-specific URI mapping.

Limitations

  • Changes appear per tool call, not as the agent streams them: the agent writes each edit to disk at once.
  • An Undo waits up to 30 seconds for the agent's edit of the file in progress, then asks to try again. A file counts as being edited from the agent's pre-tool hook until its edit is recorded, including while a permission prompt waits.
  • Undo saves unsaved changes without asking, and refuses lines the user also changed.
  • Dirty reconciliation automatically handles only non-conflicting changes with a known synchronized ancestor. Overlaps still require user resolution; it is not full parity with local's model-first editing of unsaved text.
  • While the editor-review feature or an existing review owns retention, normally sized models retain one shared immutable last-synchronized text copy. No saved-text copy is retained without a consumer. Models too large for synchronization, restored dirty documents without a known ancestor, and files already dirty when retention first starts cannot be automatically reconciled.
  • Host write coordination remains shared across clients independently of the editor setting; the opt-in controls editor review and its baseline-retention owners, not the host's ordering guarantees.
  • Keeping or undoing a renamed file reports the host's error.
  • Notebooks, non-UTF-8 files and deleted files aren't reviewed in the editor.
  • Only Copilot's edit tool calls wait for an Undo, so other providers keep Undo disabled while a turn runs; Keep stays available.
  • Review preparation and hunk mapping use worker diffs with a 1 second computation limit. Timed-out or stale hunk mappings are refused rather than guessing their lines.
  • Telemetry attributes Keep and Undo to the chat's latest request.
  • Editor review requires an advertised Working Set and its supported operations. The bridge is provider/connection-independent; in-turn destructive operations still require an actual host/provider write barrier. Broader request/checkpoint undo is outside this PR.

Open questions

Changes

Area Files
Review layer (new) agentHostEditReview.ts (Working Set entries, displayed-nonce operations, range mapping, synchronization gating, cancellation, conflict notification and save/revert recovery)
Wiring agentHostSnapshotController.ts (delegation, reactive capabilities, replacement-safe review ownership), agentHostSessionHandler.ts (attach to eligible chat-view/editor sessions using catalog roles, without local/provider gates), chatEditingService.ts / chatEditingServiceImpl.ts (observable review/global-session capabilities and their editor/inline-chat consumers; storeState and entry lookup only use ChatEditingSessions)
Shared editing chatEditingModifiedDocumentEntry.ts (_resetOriginalContent, an opt-in edit-mapping hook, and overridable diffInfo), chatEditingTextModelChangeService.ts (accepts the review's initial mapping without changing local-chat defaults), chatEditingModifiedFileEntry.ts / chatEditingService.ts / chatMarkdownContentPart.ts (being-modified state can omit a response), chatEditingCodeEditorIntegration.ts (the Toggle Diff autorun ends with its integration, so it no longer leaks when an entry is disposed undecided)
Dirty-file synchronization textFileEditorModel.ts / textFileEditorModelManager.ts / textfiles.ts (reference-counted ancestor retention, explicit reconciliation API, worker diff/rebase, stale-operation guards, dirty/undo tracking and exact-version save metadata); agentHostSessionHandler.ts and agentHostEditReview.ts own the scoped retention lifetimes
Accessibility Existing editor and working-set Keep/Undo guidance is reused without redundant implementation-specific copy; review operations reuse the existing accessibility signals
Setting constants.ts, chat.shared.contribution.ts
Working set agentHostSessionInputPills.ts (Keep and Undo echo the optional nonce retained with the displayed diff, including when only a nonce changes)
Protocol adoption Generated changeset file/target types and .ahp-version synced to merged AHP 1d639930; optional nonce and isEditing fields, no new capability or protocol-version bump
Agent host: Working Set agentHostChangesetService.ts (refreshes after each edit; publishes standard nonces and editing state, clears editing state with current changes and skips superseded computations), sessionDiffAggregator.ts (computeWorkingSetFiles and file nonces), agentHostChangesetOperationService.ts (Working Set operations while a turn runs), agentHostAcceptChangesOperationHandler.ts / agentHostDiscardChangesOperationHandler.ts / workingSetNonce.ts (optional nonce checks, standard Conflict errors, and discards while a turn runs)
Agent host: edit coordination agentHostFileEditCoordinator.ts (new), shared fileEditTracker.ts (observed/coordinated edits and snapshot-owned lifetimes), claudeFileEditObserver.ts (shared observed tracking), copilotAgentSession.ts / copilotSessionLauncher.ts / copilotPluginConverters.ts (SDK hooks await coordinated snapshots; the agent is told about Undos), agentHostServices.ts
Tests agentHostEditReview.test.ts and agentHostFileEditCoordinator.test.ts (new); changeset-service and editor-review tests cover standard editing state, false/omitted compatibility and fresh-publication ordering; textFileEditorModel.test.ts (real file-model merge/save/undo, conflicts, stale computations, one-read metadata validation, EOL/BOM and saved-version regressions); fileEditTracker, Claude observer/router, Copilot lifecycle, remote/provider eligibility, opaque chat roles, revision-bound deferred completion, reactive capabilities/replacement disposal, accessibility, Accept and Discard Changes handler, changeset services and plugin converter tests

How to test

  1. Set "chat.editor.keepUndo.enabled": true and "chat.editor.workingSet.enabled": true, then open a new chat or reopen an existing one so the editor-review setting takes effect.
  2. In a local Copilot (Agent Host) chat in a git repository, ask the agent to edit a file and create another one.
  3. Editor: open the edited file from the working set. The agent's hunks are highlighted with Keep/Undo, the overlay shows Keep / Undo / n of m, and ⌘Y / ⌘N (Ctrl+Y / Ctrl+N on Windows and Linux) keep or undo the hunk at the cursor.
  4. Hunk Undo: after the turn, undo one hunk. It reverts on disk, and the working set and Session Changes drop it.
  5. Keep: keep the remaining hunks. The file leaves the working set and the editor review, and stays unchanged on disk.
  6. Created file: select Undo in its overlay. The file is deleted and leaves the working set.
  7. Working set: Keep or Undo a file in the working set. The editor review follows.
  8. Saved user edits: type into a file the agent edited, away from its changes, and save it before asking for another edit. Your text isn't highlighted, and Undo preserves it.
  9. While the agent works: ask for several edits with a pause between them, such as a sleep 30 command. Each change is highlighted as soon as the host records it. Keep or Undo a hunk before the turn ends: it applies, and the agent is told about the Undo with its next tool result. While the agent writes a file, that file's editor is read-only and its Keep and Undo stay disabled until the latest synchronization and diff finish. Other files remain reviewable.
  10. Legacy working set: with chat.editor.workingSet.enabled off, the legacy working set lists the files with Keep/Undo.
  11. Dirty user edits: turn Auto Save off. Ask the agent to pause before editing an open file, then type into an unrelated line and leave it unsaved before the edit tool runs. Sending a request may save existing dirty files, so make the unsaved edit during the pause or, for a file not yet under review, while its write permission waits. The agent's change should appear with Keep/Undo alongside the unsaved text, while disk still lacks the user-only change. A normal Save should persist both without an overwrite warning. Keeping the agent change should not itself save the user-only change.
  12. Conflict and recovery: repeat with an unsaved edit overlapping the agent's target. The buffer should remain intact with a warning rather than an automatic winner. Resolve the file and save or revert it; editor review should resume.
  13. Later external write: after a successful dirty-buffer reconciliation, change the file on disk again without incorporating that new version. Saving the still-dirty buffer should retain the ordinary conflict protection.
  14. Cancelled diff close: open the review diff, close any ordinary editor tab for that file, and leave a user edit unsaved in the diff. Keep the agent's change and cancel the ensuing save/close prompt. The diff and its original should remain readable. Save and close it later; it should close normally without leaving the file read-only.
  15. Other providers and remote hosts: repeat file review with a connection that advertises a Working Set. The editor should follow its advertised changes and send operations back to that host. Providers without a pre-write barrier must retain their host-advertised in-turn Undo restrictions.

Agent Host (Copilot SDK) chats lost the local harness's Keep and Undo when their editing session was reduced to checkpoints. The host already snapshots every edit tool call, so the client builds a review from those snapshots with the same per-file entries local chat uses. The chat's editing session exposes them, so the existing editor overlay, hunk actions, keybindings, and working sets light up unchanged.

Behind chat.editor.keepUndo.enabled (experimental, off by default), for chats in the chat view or an editor on the local agent host. Keep only moves the review baseline. Undo reverts the change on disk as a user edit, which the host does not record yet, so Session Changes keeps showing undone edits until the agent edits that file again.

The session working set shows changes waiting for review in a To Review tab next to Session Changes, with the local working set's actions. The host now reports created files without a before state, as restored history already did.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 19, 2026 02:48
@github-actions

github-actions Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Base: 052acf01 Current: 483e4a13

No screenshot changes.

Copilot AI left a comment

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.

🟡 Changes recommended

Unresolved Undo safety and review lifecycle issues can lose user content or leave incorrect disk state.

6 open issues
What changed in this PR

Adds an opt-in prototype restoring per-file and per-hunk Keep/Undo for local Agent Host chats, reusing the existing chat editing infrastructure.

Changes:

  • Builds review entries from host snapshots and live documents.
  • Adds a To Review tab, review actions, and accessibility guidance.
  • Updates created-file reporting and adds tests and fixtures.
File Description
src/​vs/​workbench/​test/​browser/​componentFixtures/​chat/​renderChatInput.ts Supplies review actions to fixtures.
src/​vs/​workbench/​test/​browser/​componentFixtures/​chat/​chatInput.fixture.ts Adds review layouts.
src/​vs/​workbench/​contrib/​chat/​test/​browser/​widget/​input/​chatSessionWorkingSet.test.ts Tests review tabs and actions.
src/​vs/​workbench/​contrib/​chat/​test/​browser/​widget/​input/​chatInputPart.test.ts Tests pending-review visibility.
src/​vs/​workbench/​contrib/​chat/​test/​browser/​agentSessions/​agentHostChatContribution.test.ts Tests review eligibility.
src/​vs/​workbench/​contrib/​chat/​test/​browser/​agentHost/​agentHostSnapshotController.test.ts Tests delegation and checkpoint cleanup.
src/​vs/​workbench/​contrib/​chat/​test/​browser/​agentHost/​agentHostEditReview.test.ts Adds review behavior tests.
src/​vs/​workbench/​contrib/​chat/​test/​browser/​accessibility/​chatAccessibilityHelp.test.ts Tests review help visibility.
src/​vs/​workbench/​contrib/​chat/​common/​editing/​chatEditingService.ts Passes session scope to providers.
src/​vs/​workbench/​contrib/​chat/​common/​constants.ts Defines the experimental setting.
src/​vs/​workbench/​contrib/​chat/​browser/​widget/​media/​chat.css Styles review tabs and headings.
src/​vs/​workbench/​contrib/​chat/​browser/​widget/​input/​chatSessionWorkingSet.ts Implements review tabs and controls.
src/​vs/​workbench/​contrib/​chat/​browser/​widget/​input/​chatInputPart.ts Connects review state to working sets.
src/​vs/​workbench/​contrib/​chat/​browser/​widget/​chatContentParts/​chatChangesSummaryPart.ts Supports row menus and custom opening.
src/​vs/​workbench/​contrib/​chat/​browser/​chatEditing/​chatEditingTextModelChangeService.ts Passes events to attribution checks.
src/​vs/​workbench/​contrib/​chat/​browser/​chatEditing/​chatEditingServiceImpl.ts Updates provider creation and persistence filtering.
src/​vs/​workbench/​contrib/​chat/​browser/​chatEditing/​chatEditingModifiedDocumentEntry.ts Exposes review attribution hooks.
src/​vs/​workbench/​contrib/​chat/​browser/​chat.shared.contribution.ts Registers the disabled-by-default setting.
src/​vs/​workbench/​contrib/​chat/​browser/​agentSessions/​agentHost/​agentHostSnapshotController.ts Delegates review operations.
src/​vs/​workbench/​contrib/​chat/​browser/​agentSessions/​agentHost/​agentHostSessionHandler.ts Connects turns and edits to review.
src/​vs/​workbench/​contrib/​chat/​browser/​agentSessions/​agentHost/​agentHostEditReview.ts Implements client-side review lifecycle.
src/​vs/​workbench/​contrib/​chat/​browser/​actions/​chatAccessibilityHelp.ts Documents review navigation.
src/​vs/​platform/​agentHost/​test/​node/​fileEditTracker.test.ts Tests created-file payloads.
src/​vs/​platform/​agentHost/​node/​shared/​fileEditTracker.ts Omits before-state for created files.

Review effort: Balanced

- Editing an agent change keeps it, so undoing never discards the user's
  text. The local harness and notebooks keep composing user edits into the
  agent change.
- Undoing a created file deletes it only when its baseline is empty, and
  otherwise restores the baseline.
- Drop the review of files the agent deletes or renames, or that are gone,
  so that undo never writes a stale copy back.
- Check again for unsaved changes and other reviewers after reading an edit.
- Review only UTF-8 documents, because the agent host decodes its snapshots
  as UTF-8.
- Undoing a single change keeps the entry alive until it is saved, even when
  the review ends meanwhile.
- Stop reviewing a file once the user's edits leave nothing to decide.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Keep the session working set's tab and focus while the chat widget
  re-renders without its editing session before each update.
- Stop attributing reloads to the agent after a turn even when a file
  leaves the review while the turn ends, and never touch a dropped entry.
- Do not review files whose content before the edit was not UTF-8 text,
  since the agent host decodes snapshots as UTF-8.
- Do not review notebooks, check unsaved changes in every editor, and
  only reveal changes in place in editors that can show them.
- Review files under their canonical URIs, so differently cased paths
  can't bypass the unsaved-changes check or break lookups.
- Review the edits of subagents that run on after their parent's turn,
  while their own turn runs.
- Keep the whole change a user edits part of, as the editor shows it,
  so that undoing never mixes the agent's text with the user's.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The working set on main now lists the host's Working Set changeset and
keeps or undoes its files through the agent host, so the Session Changes
| To Review tabs, the To Review list and its row menu are no longer needed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…-research-prototype

# Conflicts:
#	src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostSnapshotController.ts
#	src/vs/workbench/contrib/chat/test/browser/agentSessions/agentHostChatContribution.test.ts
The agent host now records what the user keeps: its Working Set changeset
lists the tool edits that haven't been kept, and its Accept Changes and
Discard Changes operations keep or undo a file's changes or a hunk's. The
editor working set on main uses them for files.

The editor review now follows the same Working Set, so the editor and the
working set always show the same changes, like local chat edits:

- Each review entry is a file of the Working Set, compared with its
  baseline. Changes the session hasn't recorded, like the user's typing,
  are moved into the baseline as the host does, and line endings follow
  the document.
- Keep and Undo for a file, and for a hunk in the editor, ask the host to
  accept or discard them. A hunk's lines are mapped from the document to
  the content the host recorded. A created file is kept or undone whole.
  Undo saves unsaved text first, since the host reverts on disk.
- The review no longer brackets turns, reads tool snapshots, or attributes
  reloads, and the shared change service no longer keeps agent changes the
  user edits: the host keeps undo from discarding the user's text.
- Until the first diff, the change service maps all differences as one
  change, so that typing meanwhile can't corrupt the baseline.
- Local sessions no longer adopt the entries of provider sessions.

The fileEditTracker change for created files is reverted; the review no
longer reads tool-call edits.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The agent host disables Working Set operations while a turn runs. Tell
the user to retry once the agent has finished, and report a missing
operation separately.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

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.

The autorun that closes a chat edit's diff editors once its changes are
kept or undone was only disposed by that decision, so it leaked whenever
its entry was disposed undecided. The Agent Host review disposes an entry
as soon as the host settles its file, which made this common.

The review now also closes the diff editors that compare with a leaving
entry, like local chat does once the last change is kept or undone, and
disposes the entry only after they let go of its original.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- The Working Set refreshes after each edit tool call instead of 5 seconds
  after edits pause, so its changes show while a turn runs.
- Each Working Set file publishes the version of its changes, and whether
  an agent's edit tool call is writing it, in namespaced _meta.
- Accept Changes stays available while a turn runs: it only covers the
  version the client showed, or the one last published.
- Discard Changes stays available while a turn runs for providers whose
  edit tool calls wait for it. Copilot's do, through the new file edit
  coordinator: a discard waits for the edits of its file in progress, and
  the next ones wait for the discard, so each is recorded in order.
- The agent is told about changes the user undid, once, as
  additionalContext on its next tool result or prompt.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Keep and Undo name the version of the changes the editor and the
  working set showed, so the agent host refuses changes made since.
- A file the agent is writing is read-only and can't be kept or undone,
  as while local chat edits stream into it.
- The editor reloads a document from disk before comparing it with the
  Working Set, unless it has unsaved changes, and counts the reload as
  the agent's, as local chat does once an agent's edits on disk end. The
  Working Set can now update before the editor reloads the agent's
  write, which the review otherwise took for the user's typing and
  showed no changes for.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Addresses review feedback on the editor's Keep and Undo for Agent Host
chats:

- An operation waits behind earlier ones and Working Set updates, which
  can update the entry meanwhile. It now captures the changes shown when
  the user decided, refuses if they changed, and names their version, so
  it no longer covers an edit the agent made after the click.
- Keeping or undoing changes plays the editsKept and editsUndone
  signals, once for several files, as local chat edits do, and not for
  changes the agent host refused.
- Undo stops when the document's unsaved changes can't be saved, as on a
  write conflict, instead of reverting the disk under them.
- A hunk whose lines can't be matched with the recorded content, as when
  their diff times out, is no longer kept or undone at its unmapped lines.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep the per-file review gate through Working Set refreshes and editor diffs. Preserve background edit leases until snapshots are recorded, and deny hooks whose turns ended while they waited. Initialize Agent Host edit mappings without changing local-chat defaults.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Resolve the chat test helper by preserving upstream steering-message handoff options and the editor review provider tracking.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@justschen

Justin Chen (justschen) commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator Author

The following commit, 56542957ed0, adds dirty-file reconciliation to the existing Agent Host editor Keep/Undo review.

Previously, an SDK edit could update the file on disk while an open editor still held unsaved user changes. Reloading would risk losing those changes, so the editor stayed stale: the agent's hunks were missing, and the next save could produce the "file is newer" warning.

Changes to text-file model

Displaying the combined text is only half the fix. The editor also needs to know which disk version it incorporated, whether the buffer is still dirty, and which undo-stack version represents saved content. Those responsibilities belong to the file model, not the chat review component.

The merge needs three versions:

                  Last synchronized text
                    /               \
          User's unsaved edits    Agent's disk edits
                    \               /
                       Combined text

For example:

Version Agent's setting User's setting
Last synchronized file before saved
Dirty editor before my change
Agent's disk write after saved
Reconciled editor after my change

The editor now shows the agent's change with Keep/Undo while the user's independent change remains unsaved. Disk still contains saved for the user's setting until a normal Save.

What changed in the text-file layer

  • Contract: textfiles.ts adds an explicit reconciliation operation. It reports Reconciled, Conflict, Stale, or Unavailable, allowing the caller to handle an overlap, a concurrent change, or a missing safe ancestor without forcing a reload.
  • A real common ancestor: textFileEditorModel.ts retains immutable text from the last synchronized disk version, including successful saves. Disk metadata and a saved undo-version marker alone cannot reconstruct that text. This ancestor is separate from the Working Set baseline: "what the editor last synchronized with" is not necessarily "what agent changes remain unkept."
  • Merge without saving or replacing the model: worker diffs compare the ancestor with the buffer and incoming disk content. Non-conflicting external edits are rebased over user edits and applied through normal text-model operations, preserving undo history and current line endings.
  • Correct save bookkeeping: the model checks buffer versions, save state, encoding, cancellation, and the expected disk content/version before accepting the result. It advances save metadata to the incorporated disk version while keeping the buffer dirty if unsaved differences remain. A normal Save can persist both changes; a later, unseen disk write still receives normal conflict protection.

Updating only the text would leave stale save metadata and the overwrite warning. Merely refreshing the ETag could hide an unincorporated disk change. The file-model operation coordinates both rather than having chat manipulate private save-state fields.

What stays in the chat layer

agentHostEditReview.ts selects the recorded Working Set version, attributes the incorporated edit to the agent, updates the existing review UI, and handles gating, notifications, and recovery. The text-file model itself does not know about Copilot, Working Sets, or Keep/Undo.

Overlaps or unsafe comparisons preserve the user's buffer and warn instead of guessing a winner. Editor review resumes after resolution and save/revert, or a later Working Set update that can be reconciled safely.

Scope and safeguards

The SDK still writes to disk, and the host's Working Set still owns Keep/Undo decisions. Only the opt-in Agent Host editor review requests automatic reconciliation; ordinary dirty files do not start auto-merging.

There is a shared-model cost: retaining one additional immutable text copy for eligible models. Models too large for synchronization are excluded; restored dirty documents without a known ancestor cannot be automatically reconciled.

Reconciliation does not implicitly save user changes. Keep leaves them unsaved; existing send-time saving and host Undo's save-first behavior remain unchanged. The SDK still does not automatically see unsaved editor text when deciding what to edit.

The commit includes regression coverage and accessibility guidance, plus the council-review fixes: immutable ancestors across EOL changes, recovery from clean reload failures without leaving the editor read-only, and preserved undo-to-saved tracking when reconciliation is a no-op.

Incorporate non-conflicting SDK disk edits into dirty editor buffers using an immutable synchronized baseline. Preserve unsaved user text and undo history while retaining save-conflict protection for later disk changes.

Recover editor review after synchronization failures and include real file-model regressions for stale updates, EOL changes, save behavior, and undo-to-saved tracking.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@justschen Justin Chen (justschen) changed the title agentHost: prototype keep/undo review for agent edits implement keep/undo in copilot harness Oct 21, 2026
Combine upstream generated-image result handling with pending Undo context and failure hooks, preserving the SDK's post-tool plugin ordering. Cover both outputs together and register the text-file events required by the editor review test harness.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep review originals alive until their diff editors close, release editing restrictions on teardown, and scope saved-content retention to opted-in review owners.

Run review diffs and hunk mapping through the editor worker with stale-result guards. Require client-supplied versions for active-turn Keep/Undo while preserving versionless idle behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The generic file-service mock creates a new ETag on every read, so reconciliation can correctly report stale input when the retention test crosses a clock tick. Use an in-memory filesystem with write-based versions and deliberately delayed reads instead, settling initialization events before constructing the model manager.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread src/vs/workbench/services/textfile/common/textFileEditorModel.ts Outdated
Comment thread src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts Outdated
Comment thread src/vs/platform/agentHost/common/meta/vscode/agentWorkingSetFileMeta.ts Outdated
* ones wait for it, so each edit's snapshots and the host's change are
* recorded in the order they were made.
*/
export interface IAgentHostFileEditCoordinator {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should the FileEditTracker logic be folded into here? We use that for Claude and Codex and then they should be able to get the tracking for 'free' here.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Moved edit-lifetime ownership into the shared FileEditTracker in 483ab4f, so its callers get editing state and snapshot-lifetime cleanup without a separate provider-owned lease map. Copilot awaits coordinated acquisition; Claude marks its observed writes synchronously and holds the mark through persistence. I kept the coordinator separate from snapshot/database/diff/telemetry responsibilities rather than moving the entire tracker into that service.

One distinction in the current code: Codex uses its native turn-diff pipeline (CodexTurnDiffTracker), not FileEditTracker. That authoritative path is unchanged, and the editor now accepts an advertised Working Set regardless of provider. Claude message-stream observation cannot actually pause SDK writes, so it deliberately does not register as a coordinated provider or enable unsafe in-turn Undo. Shared-tracker and Claude observer regressions cover these boundaries.

Comment thread src/vs/platform/agentHost/node/copilot/copilotAgentSession.ts Outdated
Bind deferred Keep/Undo settlement to the displayed revision, make review capabilities reactive, and dispose replaced reviews. Follow advertised Working Sets and chat origins across providers and connections.

Move snapshot-owned edit lifetimes into the shared tracker, preserve SDK work across root-turn transitions, and avoid claiming a pre-write barrier for observational providers. Revalidate dirty-file synchronization with stat instead of a second content read.

Add revision-race, provider/remote, ownership, and late-completion regressions. Protocol field proposal: microsoft/agent-host-protocol#538.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Resolve the chat contribution test helper by retaining main's editing-session factory alongside the review-provider and text-file retention stubs. Preserve current SDK updates and the scoped review fixes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@vs-code-engineering

Copy link
Copy Markdown
Contributor

📬 CODENOTIFY

The following users are being notified based on files changed in this PR:

TylerLeonhardt

Matched files:

  • src/vs/platform/agentHost/node/claude/claudeFileEditObserver.ts

Sync the protocol to AHP 53f84cd0 and replace private Working Set version metadata with optional file/target nonces. Preserve the displayed nonce across editor and working-set operations, reject stale supplied nonces with Conflict, and retain omission compatibility during active turns.

Includes stale-write, deferred-settlement, nonce forwarding and omitted-nonce regressions. Assumes microsoft/agent-host-protocol#539 will land; no negotiated protocol version or SDK bump.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Preserve both the Keep/Undo file edit coordinator and upstream state-manager dependency in the Copilot session constructor. Retain upstream changeset and hook updates alongside the optional AHP nonce adoption.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sync the vendored protocol to 4ad8caa6 after microsoft/agent-host-protocol#539 merged. Carry over the agreed nonce wording without changing protocol declarations or the negotiated version.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Combine upstream Computer Use setup callbacks and service dependencies with edit tracking, dirty-buffer retention, and their test registrations. Preserve the AHP nonce adoption while taking the current Copilot SDK and runtime.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adopt the optional ChangesetFile.isEditing field merged in microsoft/agent-host-protocol#540 and remove the private Working Set metadata helpers. Preserve fresh-publication review gating and cover explicit false and omitted editing state.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep the upstream cloud default-chat title tests alongside the editor-review and file-retention fixtures.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.

3 participants