Skip to content

[pull] main from microsoft:main#1456

Merged
pull[bot] merged 27 commits into
code:mainfrom
microsoft:main
Jul 7, 2026
Merged

[pull] main from microsoft:main#1456
pull[bot] merged 27 commits into
code:mainfrom
microsoft:main

Conversation

@pull

@pull pull Bot commented Jul 7, 2026

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

Giuspepe and others added 27 commits July 6, 2026 17:01
The agent host downloads Claude/Codex SDK binaries on first use and shows a
progress notification. Two issues:

- #324455: The notification title ended in an ellipsis, but the progress
  service renders progress as "<title>: <percent>", producing an unusual
  "…:" combination (e.g. "Downloading Claude agent…: 45%"). Drop the
  trailing ellipsis so it reads "Downloading Claude agent: 45%".

- #324461: The notification only appeared in the Agents window. The editor
  window never opted in (no progressToken on createSession) nor subscribed
  to root/progress notifications, so nothing showed there.

Extract the render logic into a shared AgentHostDownloadProgress helper used
by both the Agents window (BaseAgentHostSessionsProvider) and the editor
window (AgentHostContribution). The editor-window session handlers now pass a
progressToken on createSession, and AgentHostContribution subscribes to
root/progress — gated to !isSessionsWindow so the Agents window (which renders
progress via its own provider) does not double-notify.

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

In the editor window with no workspace folder open, `_readWorkingDirectory`
resolves `undefined` (there is no folder to fall back to), so the Codex
provisional `createSession` threw `'Codex requires a working directory'`. That
had two user-visible effects:

- The SDK download-progress notification never showed, because interest is
  only registered on a successful `AgentService.createSession` — the throw
  happened before registration, so `emitDownloadProgress` found no interested
  session and emitted nothing.
- In an empty window the session never materialized, so the chat "sat there"
  and never started.

Instead of rejecting session creation, defer the working-directory requirement:
allow `createSession` to succeed without a cwd (so the provisional exists and
the download-progress opt-in is registered), and lazily create a managed
per-session temp folder at materialize time. The folder is tracked on the
session and removed on `disposeSession`. When the client does supply a working
directory (the common case, and the Agents window), nothing changes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When a materialized Claude session is the last-active session on startup,
the renderer subscribes to it, driving the host restore path
(subscribe -> restoreSession -> getSessionMetadata/getSessionMessages).
Both of those read through the Claude SDK, which dynamically imports the
SDK module and triggered a cold download purely from restoring/preselecting
Claude — with no progress interest registered, so no notification. The
download must only start on the first user message.

listSessions was already guarded with canLoadWithoutDownload(); this adds
the matching guard to getSessionMetadata and getSessionMessages so restore
defers (undefined / empty transcript) instead of downloading. The session
re-hydrates on the next restore once the SDK is present. Codex is
unaffected: its equivalents read persisted state from disk.

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

When a new agent-host session's first message triggers a lengthy bring-up
(notably the Claude SDK download), the send parks in `_waitForNewSession`
until the backend materializes the session. If a SECOND session of the same
type is started and sent during that window, both sends park at once.

`_waitForNewSession` identified a send's committed session by novelty +
scheme. With two concurrent same-scheme sends that is ambiguous: whichever
session materializes first is grabbed by the send that parked first,
regardless of ownership. In practice the two sends SWAPPED sessions — the
first send graduated onto the second session and vice versa — so after the
download the user was left focused on the wrong session (the first one),
while their most recent session appeared to "reset".

A send's committed backend session keeps the eager id it was created with
(`chatResource` path), so match each send to its OWN id first. A novelty
fallback is retained for backends that assign a fresh id, but it now
excludes other in-flight sends' own ids so two concurrent sends can never
swap. A per-send claim set still prevents two sends from resolving to the
same session.

Codex/Copilot were not visibly affected only because their materialize is
fast enough that two sends rarely overlap; the fix lives in the shared base
provider and covers all agent-host types.

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

# Conflicts:
#	src/vs/sessions/contrib/providers/agentHost/browser/baseAgentHostSessionsProvider.ts
…in agents window (#324775)

* change the default to open editors to inside editor instead of modal in agents window

* enable experiment for this setting and address feedback
feat(stylelint): add validation for deprecated design tokens

Co-authored-by: mrleemurray <mrleemurray@users.noreply.github.com>
…items

Fix agent host SDK download progress notification (#324455, #324461) and related agent-session bugs
Introduce a `never` editor priority and make custom editors opt out of
diff and merge editors by default instead of requiring users to reset
via `workbench.diffEditorAssociations`.

- Add `RegisteredEditorPriority.never` / `CustomEditorPriority.never`
  (rank 0). A `never` editor is never auto-selected for that input type
  and is skipped by general `workbench.editorAssociations`, but can
  still be forced by `workbench.diffEditorAssociations` or picked via
  `Reopen Editor With...`.
- Resolve the `customEditors` contribution priority per input type:
  string / omitted priorities map diff and merge to `never`; only an
  explicit `diffEditor` / `mergeEditor` opts the editor back in.
- Filter general editor associations that point at a `never` editor for
  the requested input type in the editor resolver.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…d rejection (fixes #323149) (#323154)

The command github.copilot.terminal.fixTerminalLastCommand builds picksPromise via new Promise(r => generateTerminalQuickFix(...).then(fixes => { ...; r(picks); })). The .then() had no rejection handler, so when generateTerminalQuickFix rejected (it throws when a chat fetch returns a non-success result) two failures occurred: an unhandled promise rejection reported to error telemetry, and r(picks) never ran so picksPromise never settled and the quick pick was stranded on Generating. Add a rejection handler at the fire-and-forget boundary that logs the error and resolves with an empty result, reusing the existing No fixes found UX. The intentional throw at the failure site is left untouched.

Co-authored-by: vs-code-engineering[bot] <122617954+vs-code-engineering[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Clamp maxOutputTokens to the context window and maxInputTokens to the
  remaining budget in resolveModelTokenLimits so limits stay consistent
- Require either maxInputTokens or contextWindow via anyOf in the custom
  model schemas (customoai, customendpoint, azure)
- Update OpenRouter spec header to describe the fixed behavior

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When the built-in text diff editor cannot render a diff because the
content is binary, prefer a custom editor that can render a diff for the
resource instead of showing the generic "cannot display" binary panel.

This complements the `never` diff priority: a custom editor that opts out
of diffs for text files (e.g. a `priority: default` editor, which now
maps diff/merge to `never`) is still the best choice for binary diffs,
where the text diff editor is useless.

- Add `IEditorResolverService.getBinaryDiffFallbackEditor(resource)`,
  returning the best diff-capable editor (including `never` ones),
  excluding the built-in default text editor.
- In `TextDiffEditor.openAsBinary`, resolve and use that editor (via an
  explicit `override`, which bypasses the `never` filtering) before
  falling back to the binary panel. The custom diff opens in the binary
  diff pane, so there is no re-entry into the text diff editor.

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

The cell toolbar sticky-scroll onDidScroll listener can outlive the
cell's membership in the editor's view model (cell removed, or the
pooled notebook editor widget reattached to a different notebook). A
synchronous reveal-triggered scroll then delivers to the stale listener,
which dereferences the evicted cell via getAbsoluteTopOfElement and
throws 'ListError [NotebookCellList] Invalid index -1'.

Re-resolve the captured cell against the current view model before
positioning and skip the update when it is no longer present.

Co-authored-by: vs-code-engineering[bot] <122617954+vs-code-engineering[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ests

Improve context window handling for OpenRouter and Custom Endpoints
* Remove IStorageSourceFilter (#323480)

Cherry-picked from #323480's merge commit, modifying the call to
createInstance(), which no longer needs a descriptor.

* signing commit
* chat: preserve shell intention command height

Keep the command code element inline inside its flex truncation wrapper so intention rows match other inline code spans without losing ellipsis behavior. Add production-accurate component fixture coverage for height parity.\n\n(Written by Copilot)\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chat: keep shell intention chevron adjacent (#324808)

Let short terminal intention rows shrink-wrap their content so the expand chevron remains next to the command, while retaining the full-width cap and ellipsis behavior for overflowing rows.\n\n(Written by Copilot)\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Revert chevron positioning from height fix

Keep the chevron positioning change in a separate pull request targeting main.\n\n(Written by Copilot)\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
[cherry-pick] [release/1.128] Hide Tools Marketplace in sessions window
Let short terminal intention rows shrink-wrap their content so the expand chevron remains next to the command, while retaining the full-width cap and ellipsis behavior for overflowing rows.\n\n(Written by Copilot)\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…4817)

* Revert 'chat: keep sticky scroll with pending messages' (PR #324087)

This reverts commit 6e354ab, which caused a regression (#324766) where steering/queued messages became sticky to the bottom of the viewport. Reverting to rework on a correct fix.

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

* chat: follow scroll and hug pending rows for streaming response

PR #324087 fixed the active response no longer following streamed content when a queued/steering row was shown below it, but it did so by reserving the viewport-filling min-height on the streaming response (via the sticky-scroll target). That pushed the queued/steering rows a full viewport down (#324766).

This keeps only the correct part of that change - progressive rendering and errorDetails 'isLast' target the last non-pending response, so it keeps streaming with pending rows below - and drops the min-height/layout retargeting, so no reserved space is inserted between the response and the pending rows (they hug it).

Follow is fixed at its real root cause: scrollToEnd revealed the held _lastItem by reference and guarded on tree.hasElement(). Pending rows are recreated on every getItems() with a stable dataId, so _lastItem diverged from the instance in the tree, hasElement() was false, and the reveal silently no-oped. scrollToEnd now reveals the tree's actual last node instead.

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

* unsalt

---------

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

Co-authored-by: vs-code-engineering[bot] <vs-code-engineering[bot]@users.noreply.github.com>
* Add prompt timeline rail to the Agents window

Introduce a right-edge "prompt timeline" rail in the Agents window that
lets users scan and jump between the prompts they have sent in a chat
session, adapted from GitHub's "jump to your messages" affordance.

- Self-contained contrib under src/vs/sessions/contrib/promptTimeline,
  attached per-widget via IChatWidgetContrib (no vs/workbench changes).
- Recency-bucketed ticks (pure, unit-tested budgetBucketPrompts) capped
  so each tick keeps a >=24px hit target (WCAG 2.5.8).
- Dense marks at rest that expand on hover/focus, with green/red per-turn
  diff magnitude and a solid-blue active mark.
- Interactive hover card: prompt text, clickable +/- diff stats that open
  the per-request diff, and per-file drill-down rows.
- Per-request diffs via provider-agnostic IChatResponseFileChangesService
  (agent-host server-computed turn changeset) with editing-session
  fallback; diff sides are probed so missing checkpoint blobs render as a
  pure add/delete instead of crashing the multi-diff editor.
- Keyboard-first commands: Go to Next/Previous Prompt, Go to Prompt...
  (quick pick), Review Changes for Prompt, with ARIA announcements.
- Gated behind the new sessions.promptTimeline.enabled setting and
  ChatContextKeys.enabled; rail is created/disposed reactively.

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

* Prompt timeline: quiet gray-dense rail with single-mark accordion

Replace the whole-rail expand-on-engage behavior with a per-mark
accordion. The rail now stays quiet, gray and dense at all times; only
the mark under the pointer (or keyboard focus) expands to a >=24px pill
and reveals its green/red diff, so the rail no longer becomes a column
of slim pills with large gaps when interacted with.

- CSS: drop the `.engaged` whole-rail slot expansion and the transform
  fisheye; expand only `:hover`/`:focus-visible` marks (8px -> 24px slot,
  fat pill, diff colors). Active mark stays solid blue in every state.
- Rail: remove the fisheye magnify, the engaged-state tracking, and the
  magnitude width buckets; capacity now reserves headroom for a single
  expanded mark over the dense 8px rhythm, so more prompts fit.
- Drop the now-unused --tick-scale/--tick-scale-y custom properties.

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

* Prompt timeline: open per-file diff inside the multi-diff, revealed at that file

Clicking a file row in the hover card previously opened a standalone
single-file diff. Route it through the same multi-file diff we already
build for "Review Changes", passing viewState.revealData so it opens
revealed at the clicked file. Per-file and whole-prompt review now share
one multi-diff experience (and open in the modal editor when enabled),
reusing the existing MultiDiffEditorInput without any new UI.

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

* Prompt timeline: show per-turn diffs against the frozen after-turn snapshot

The per-prompt diff was rendering before-turn vs the live working file, so
it showed the combined changes of all later turns. The per-turn changeset
already carries a frozen "after" snapshot; surface it and diff against it.

- Add optional `modifiedContentURI` to IEditSessionEntryDiff: the frozen
  after-state RHS content, distinct from `modifiedURI` (the live file used
  for identity / go-to-file). Opt-in, so existing consumers (e.g. the chat
  "Changed N files" summary) keep their current behavior unchanged.
- Populate it in AgentHostResponseFileChangesProvider from the changeset's
  after-content; extend its test to assert the mapping.
- PromptTimelineModel now diffs originalURI (frozen before) against the
  frozen after snapshot, so review shows only that turn's changes, while
  go-to-file still opens the live working file.

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

* Prompt timeline: add an overview-ruler style selectable by setting

Introduce a second rail style and let users choose via the new
sessions.promptTimeline.style setting ('compact' | 'overview').

- Extract IPromptTimelineRail as the seam the contrib consumes, and a
  shared PromptTimelineCard (hover preview) used by both styles.
- Rename the existing rail to PromptTimelinePillRail (dense pills).
- Add PromptTimelineRulerRail: the session compressed into the rail like
  the editor overview ruler — proportional marks at their transcript
  scroll positions, a scrollbar-slider you-are-here thumb, one green
  "changed code" signal (editorOverviewRuler.addedForeground), focusBorder
  active, >=24px hit targets. Detail stays in the shared hover card.
- Model exposes getScrollLayout()/onDidChangeScrollLayout for proportional
  positions (per-request offsets via currentRenderedHeight + scrollTop).
- Contrib picks the rail class reactively and reuses the enablement swap
  pattern, wiring setScrollLayout only for rails that support it.

All colors come from real theme tokens. Validated: typecheck, eslint,
layers, hygiene, unit tests (12 passing).

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

* Prompt timeline: position overview-ruler marks from the list layout model

The overview-ruler rail placed each mark by accumulating chat items'
`currentRenderedHeight`, which the virtualized tree only sets for rows it has
actually rendered. Off-screen prompts reported `undefined` (treated as 0), so
every not-yet-rendered mark collapsed onto the same position and only spread
out as the user scrolled and rows rendered.

Read each prompt's top from the list's layout height model instead, the same
virtualization-safe source the scrollbar uses (all items have a height via the
delegate's `currentRenderedHeight ?? defaultElementHeight`). Threaded a
read-only accessor through the layers, all in the list's content-pixel space so
the viewport thumb stays aligned:

- AbstractTree.getElementTop(location): absolute top (works off-screen), sibling
  of getRelativeTop.
- ChatListWidget.getElementTop / ChatWidget.getElementTop + ChatWidget.scrollHeight.
- PromptTimelineModel.getScrollLayout() and _updateActive() now use these instead
  of accumulating currentRenderedHeight.

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

* Prompt timeline: color ruler marks by the turn's add/remove split

Ruler marks were a single green "edited" signal, so a delete-heavy turn still
showed green. Render the mark as a two-tone bar instead (a green added segment
and a red removed segment sized by flexGrow from the turn's diff stat), reusing
the pill rail's seg-add/seg-del pattern. Only the non-zero side is appended, so
a pure-add turn is fully green and a pure-delete turn fully red, and a 1px
min-width keeps the minority color visible on a lopsided split. Segments go
transparent while the mark is active so the focusBorder you-are-here color wins.

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

* Prompt timeline: fall back to the live file when a turn snapshot is unreadable

Per-turn review diffed the frozen before/after turn-checkpoint snapshots so only
that turn's changes show. But those checkpoint blobs can be absent (an added
file's original, or a pruned/restored session where whole turn checkpoints are
gone). When both sides were unreadable every item was dropped and reviewChanges
opened nothing at all.

_readableSides now still prefers the frozen snapshots but falls back to the live
working file for the modified side when the checkpoint blob is unreadable, so
review always opens with the best available fidelity. An unreadable side is
still dropped so the file renders as a clean add/delete instead of crashing the
diff editor.

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

* Prompt timeline: drop the pill style, keep only the overview ruler

The rail shipped with two selectable styles (dense pills vs overview ruler)
behind sessions.promptTimeline.style. We're standardizing on the overview-ruler
UX, so remove the pill style and the setting entirely (the on/off
sessions.promptTimeline.enabled setting stays).

- Delete promptTimelinePillRail.ts and the PROMPT_TIMELINE_STYLE_SETTING /
  PromptTimelineStyle enum.
- The contribution always builds the ruler rail; drop the style-based swap, the
  capacity->display-budget wiring, and the optional setScrollLayout indirection
  (now required on the interface).
- Model no longer tracks a dynamic display budget; ticks bucket against the
  constant MAX_TICKS (still caps very long sessions).
- Remove the pill-only CSS; the overflow rule now hides the ruler marks.

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

* Prompt timeline: address review feedback on grouped-tick diffs and read probes

Two fixes from a council review:

- getRequestFiles: when a grouped tick edits the same file in more than one
  prompt, advance the merged entry's diffModifiedURI to the later prompt's
  after-snapshot (keeping the earliest originalURI) so the opened multi-diff
  spans the whole tick instead of only the first edit.
- _canRead: probe availability with readFile(resource, { length: 1 }) instead of
  reading whole (potentially large) file contents just to test whether a diff
  side is readable.

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

* Prompt timeline: refresh comments left over from earlier iterations

Comment-only cleanup after removing the pill style and the display-budget
mechanism: drop the stale "or budget", "(like the pill rail)", and "visual
density" references so the docs match the single overview-ruler implementation.

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

* Prompt timeline: gate the enabled setting behind an experiment

Put sessions.promptTimeline.enabled behind the experimentation service via the
`experiment: { mode: 'startup' }` property (the sanctioned way for core
TS-registered settings; the registry auto-adds the onExP tag). Default is now
false so the rail is off until the experiment (or the user) turns it on.

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

* Prompt timeline: address PR review feedback and fix class-field init order

Review fixes:
- Remove the redundant GoToNext/GoToPrevious commands: they duplicated and
  collided with the built-in chat nextUserPrompt/previousUserPrompt keybindings
  (same chord, weight, when). Drop the two actions, their command ids,
  contrib.navigate(), and model.getSiblingTick(). GoToPrompt and ReviewChanges
  stay.
- Fix the active-mark fallback: when scrolled above the oldest prompt, highlight
  the oldest tick (ticks.at(0)), not the newest (ticks.at(-1)).
- Replace the getComputedStyle probe + unreverted host `position` mutation with a
  lifecycle-scoped `.prompt-timeline-host` class removed on teardown.
- Give the overview-ruler toolbar a proper keyboard model: aria-orientation,
  roving tabindex (a single Tab stop) and Arrow/Home/End navigation between marks.

Also assign `_sessionResource` in the constructor instead of a field initializer:
it reads `this.widget`, which under useDefineForClassFields is not yet assigned
when field initializers run (fixes the define-class-fields-check / TS2729).

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

* Prompt timeline: stop showing the unreliable per-prompt timestamp

Agent-host sessions don't record per-turn timestamps; the transcript is
reconstructed via ChatModel.addRequest(), which stamps Date.now(), so every
prompt showed ~the current time. Since the real send times aren't available to
recover, drop the absolute time from the hover card and the "Go to Prompt"
picker. Grouped ticks still show their prompt count; the prompt text and diff
stats (both reliable) are unchanged.

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

* Prompt timeline: act on PR review (remove ReviewChanges action; rename modifiedContentURI)

- Remove the ReviewPromptChangesAction command: per-prompt review is already
  available from the hover card, so the palette/keyboard entry point was
  redundant. Drops the command id and the now-unused contrib.getActiveTick() /
  contrib.reviewChanges() bridge (model.reviewChanges stays, wired to the card).
- Rename IEditSessionEntryDiff.modifiedContentURI -> modifiedSnapshotURI: it's the
  frozen after-turn snapshot content, and "snapshot" reads clearer next to
  modifiedURI (the live file) than "content".
- Clarify in comments that this field is distinct from the agent-host checkpoint-
  ref readability fix (#323932): that made the snapshot blobs readable; this
  carries which snapshot to diff against so a per-turn review shows only that
  turn's changes.

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

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@pull pull Bot locked and limited conversation to collaborators Jul 7, 2026
@pull pull Bot added the ⤵️ pull label Jul 7, 2026
@pull pull Bot merged commit 04c9ab5 into code:main Jul 7, 2026
1 check failed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants