Skip to content

Enhance draft input persistence for remote chat sessions#311312

Draft
DonJayamanne wants to merge 1 commit intomainfrom
don/interior-swan
Draft

Enhance draft input persistence for remote chat sessions#311312
DonJayamanne wants to merge 1 commit intomainfrom
don/interior-swan

Conversation

@DonJayamanne
Copy link
Copy Markdown
Contributor

Improve the handling of unsent draft inputs in remote chat sessions. Ensure that draft inputs are preserved when switching sessions, allowing users to retain their input state. Add tests to verify that draft inputs are correctly restored after disposing and reloading a session.

Fixes #311310

Copilot AI review requested due to automatic review settings April 20, 2026 05:46
@DonJayamanne DonJayamanne self-assigned this Apr 20, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Screenshot Changes

Base: 341ef7db Current: fc39d444

Changed (5)

chat/aiCustomizations/aiCustomizationManagementEditor/McpBrowseMode/Dark
Before After
before after
editor/inlineCompletions/other/JumpToHint/Dark
Before After
before after
agentSessionsViewer/CompletedUnread/Dark
Before After
before after
agentSessionsViewer/CompletedUnread/Light
Before After
before after
agentSessionsViewer/ApprovalRowLongLabel/Light
Before After
before after

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR improves draft (unsent) chat input persistence for remote/external chat sessions so that switching away from a session and back restores the user’s pending input state. It also adds a regression test validating draft restoration after disposing/reloading a remote session.

Changes:

  • Persist serialized draft inputState for external sessions in session metadata and restore it when loading remote sessions.
  • Persist external session metadata on dispose when there are requests or an unsent draft.
  • Add a unit test covering draft restoration for a remote session after disposal/reload.
Show a summary per file
File Description
src/vs/workbench/contrib/chat/test/common/chatService/chatService.test.ts Adds a test to ensure a remote session’s draft input text is restored after dispose/reload.
src/vs/workbench/contrib/chat/common/model/chatSessionStore.ts Extends session metadata to include serialized draft inputState for external sessions.
src/vs/workbench/contrib/chat/common/chatService/chatServiceImpl.ts Persists metadata for external sessions when there’s a draft; restores draft from stored metadata on remote session load.
src/vs/workbench/contrib/chat/browser/widget/input/chatInputPart.ts Adds a public method to flush view input state into the bound input model.
src/vs/workbench/contrib/chat/browser/widget/chatWidget.ts Calls the flush helper before dropping the model reference (currently only in the setModel(undefined) path).

Copilot's findings

Comments suppressed due to low confidence (1)

src/vs/workbench/contrib/chat/common/chatService/chatServiceImpl.ts:210

  • The dispose-time condition model.getRequests().length > 0 || hasDraftInput(model) can leave stale metadata.inputState behind for empty external sessions. Example: type a draft (persisted), reopen, clear the draft back to empty, switch away -> hasDraftInput becomes false and metadata is no longer written, so the old draft will be restored again next open. Consider persisting metadata for external sessions when there is any existing stored inputState to clear, or basing the decision on whether inputModel.toJSON() has changed rather than only whether it’s currently non-empty.
				} else if (!localSessionId && (model.getRequests().length > 0 || hasDraftInput(model))) {
					// External sessions: persist metadata when there are requests, OR when the
					// user has typed/attached unsent input we need to restore on next open.
					await this._chatSessionStore.storeSessionsMetadataOnly([model]);
				}
  • Files reviewed: 5/5 changed files
  • Comments generated: 3

Comment on lines 1975 to 1979
if (!model) {
// Flush any unsent draft to the outgoing input model before we drop our
// reference to it, so the host's `willDisposeModel` persistence sees it.
this.inputPart.flushInputStateToModel();
if (this.viewModel?.editing) {
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

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

setModel() flushes draft input only when called with undefined. During normal session switches (old model -> new model), callers pass the new model directly, so the outgoing draft may never be synced to the model before the reference is released and willDisposeModel runs. Consider flushing whenever the session resource is changing (i.e. before switching away from an existing viewModel), not only when clearing the model.

Copilot uses AI. Check for mistakes.
Comment on lines +65 to +79
/**
* True when the user has typed text or attached non-trivial context to the input
* but not yet sent it. Used to decide whether an external session needs metadata
* persisted on dispose so the draft survives switching sessions.
*/
function hasDraftInput(model: ChatModel): boolean {
const state = model.inputModel.state.get();
if (!state) {
return false;
}
if (state.inputText.trim().length > 0) {
return true;
}
return state.attachments.length > 0;
}
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

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

hasDraftInput() only considers non-whitespace text and attachments. If the user changes other input state (e.g. mode, selected model, permission level, or contrib state) without text/attachments, the session can still have meaningful draft state but metadata won’t be persisted for external sessions with no requests, so those selections can be lost when switching sessions.

This issue also appears on line 206 of the same file.

Copilot uses AI. Check for mistakes.
Comment on lines +841 to +846
const isExternal = session instanceof ChatModel && !LocalChatSessionUri.parseLocalSessionId(session.sessionResource);
// Persist draft input state only for external sessions; local sessions already
// have their full state serialized via storeSessions, so duplicating here would
// be wasteful and risk drift between the two locations.
const inputState = isExternal ? (session as ChatModel).inputModel.toJSON() : undefined;

Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

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

inputState is stored in the session index (storageService) for external sessions. Since InputModel.toJSON() exports attachments via IChatRequestVariableEntry.toExport, this can include base64-encoded Uint8Array payloads (e.g. pasted images), significantly increasing the size of the index entry. External sessions are also excluded from trimEntries(), so this data can accumulate without bound. Consider filtering/limiting binary attachment data for metadata storage, or storing draft input state in a separate, size-managed location.

Copilot uses AI. Check for mistakes.
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.

Input State no persisted when using Copilot CLI

2 participants