Skip to content

[pull] main from microsoft:main#1372

Merged
pull[bot] merged 17 commits into
KingDEV95:mainfrom
microsoft:main
May 1, 2026
Merged

[pull] main from microsoft:main#1372
pull[bot] merged 17 commits into
KingDEV95:mainfrom
microsoft:main

Conversation

@pull
Copy link
Copy Markdown

@pull pull Bot commented May 1, 2026

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 : )

mjbvz and others added 17 commits April 30, 2026 20:39
We shouldn't look at the scheme directly
These belong to a follow up PR
Doesn't work 100% nicely as once toggled back it switch back to the normal inline editor instead of back to the side by side diff view. Will follow up

Co-authored-by: Copilot <copilot@github.com>
Use backticks for proper template literal interpolation of ${ToolName.ReadFile}.
Fix duplicate 'and and' → 'and'.
Import ToolName from registry so message stays in sync if tool name changes.

Addresses Copilot AI review feedback: keeps tool name in sync with registry.
* Add Cache Explorer view to chat debug panel

Add a new "Cache Explorer" entry under "Explore Trace Data" in the
chat debug overview. The view helps diagnose prompt-cache misses by
diffing two model-turn requests side by side.

The pure diff engine (chatDebugCacheDiff.ts) parses the input messages
JSON exposed via IChatDebugEventModelTurnContent.sections, normalizes
each message to {role, name, text, byteLength}, and produces a per-
position signature of the prompt prefix. The first position whose role,
length, or content diverges is reported as the cache break — anything
after that point cannot be served from the prompt cache.

The view (chatDebugCacheExplorerView.ts) lays out a left rail of model
turns annotated with cache hit %, A/B summary cards, the prompt
signature with the break marker, and a Components accordion that diffs
the system prompt and any divergent messages.

Sequential pairing is the default (B = current selection, A = previous
turn); click in the rail to set B and shift-click to set A. The diff
engine ships with 10 unit tests in chatDebugCacheDiff.test.ts.

* Cache Explorer iteration: rail groups, OTel-backed metrics, prompt signature bars

Iterate on the Cache Explorer view added earlier on this branch:

- Left rail groups model turns by parent request and shows the user
  prompt as the group header. Group rows are collapsible and the full
  request id is shown in the header.
- Each rail row reports agent source, cache hit %, duration, and time
  for the turn; rows with hit < 90% render the chip in red.
- Single-selection model: clicking a row sets it as the current request
  and the row above is implicitly the previous one to diff against.
- Producer plumbing: the file logger now persists copilot_chat.debug_name
  and gen_ai.response.id alongside the model-turn entry, and the
  modelTurn content carries a requestId. The summary card surfaces the
  full network requestId so it can be copied.
- Replaced the chip-style prompt signature with a horizontal role-
  colored bar visualization showing both requests on a shared scale,
  with a vertical break marker at the divergence index.
- Cache performance card replaces the pill row with a structured
  layout: cache hit headline + token reuse, where the cache broke +
  estimated lost tokens, and a one-line diff summary.
- Component diff and signature lanes use Previous/Current labels
  instead of A/B.

Refs #313608

* Cache Explorer: char-level inline diff in Components accordion

Replace the plain-text body of each Components row with a side-by-side
line + character diff rendered directly into HTML. Uses the existing
linesDiffComputers.getDefault().computeDiff() that Monaco's diff editor
also uses internally; ignoreTrimWhitespace stays off so cache-relevant
whitespace is visible.

- Each line is emitted as a div with one of three classes `context`,
  `add`, `remove` for full-line styling.
- Inner range mappings produce char-level <span> highlights inside
  added or removed lines.
- Multi-line inner range mappings are skipped for v1; the surrounding
  add/remove styling already conveys the change.
- Bounded by maxComputationTimeMs=200 so a stray giant tool-result
  diff cannot stall the renderer.

No widget, no editor instance, no layout calls; replaces the existing
two raw <div> bodies with a directly-styled HTML diff.

Refs #313620

* Cache Explorer: extract text from tool_call_response and tool_call parts

The OTel input messages format wraps tool I/O as part-level objects,
not as top-level text:

- A user/tool message that returns tool output uses
  { type: 'tool_call_response', id, response: '...' }
- An assistant message that invokes a tool uses
  { type: 'tool_call', id, name, arguments: {...} }

Until now parseInputMessages only counted parts with type === 'text',
so these messages showed up as zero-byte slots in the diff with both
sides labeled '(not present)' \u2014 confusing because tool I/O is the
single most cache-relevant content in an agentic loop.

This change pulls the response payload out of tool_call_response (and
the tool name + arguments out of tool_call) and includes them in the
normalized text we diff against. We also reclassify the row's display
role to 'tool' when the message is dominated by a tool result so the
rail / signature / accordion label it consistently.

Two new unit tests pin the extraction behaviour.

Refs #313620

* Cache Explorer: track request options + likely cache expiration

Prompt caches invalidate on more than just message-array changes \u2014
flipping tool_choice, raising reasoning_effort, switching to Claude
extended thinking, or changing the response_format all bust the cache
even when the prompt prefix is byte-identical. Surface those changes.

Producer:
- New OTel attribute copilot_chat.request.options carrying a curated
  subset of the request body. Captures tool_choice, reasoning,
  reasoning_effort, thinking, thinking_budget, output_config,
  response_format, text, truncation, context_management, the various
  penalties, store, stream, stream_options, prediction, seed,
  parallel_tool_calls, service_tier, metadata, verbosity, snippy,
  state, intent, intent_threshold, include, plus an 'extra' catch-all
  for any unrecognised top-level fields.
- Persisted onto llm_request entries in the file logger so the data
  survives session reloads.

Consumer plumbing:
- New requestOptions?: string on IChatDebugEventModelTurnContent and
  the matching DTO + ext-host class + proposed API. Read on both the
  live OTel span path and the on-disk entry path.

View:
- New 'Request Options' table renders every captured option with
  Previous and Current columns; rows whose values differ are
  highlighted with the diff-removed background. The model id is
  layered on top of the request_options blob so model swaps show up
  in the same table.
- An inline 'Options changed: ...' banner sits below the summary
  cards so the user spots option drift without scrolling.
- Cache performance card now detects the 'likely cache expiration'
  case: when the model reports 0% hit, the structural diff finds no
  prefix break, AND the option table is identical, the headline
  switches to '\u2014 likely cache expiration' with an explanation. When
  options are the only thing that changed, the break line says so
  explicitly.

Refs #313620

* Cache Explorer: address Copilot review nits from #313608 + #313602

Six small follow-ups:

- Switch truthy checks to '!== undefined' for token fields in
  chatDebugFlowGraph.ts (model-turn tooltip) so a turn with 0 input
  or output tokens still gets a tooltip line.
- Same fix for the modelTurn aria label in chatDebugLogsView.ts \u2014
  a 0-token turn now still announces 'Model turn: <model> 0 tokens'
  instead of dropping the count.
- Add the cached-tokens row to the modelTurn branch of
  formatEventDetail in chatDebugEventDetailRenderer.ts (regressed
  during a recent merge) and add the cachedTokens field to the
  existing 'modelTurn - with all fields' unit test.
- chatDebugFlowGraph tooltip also gains a 'Cached tokens: N' line
  when present.
- Restore the requestName deserialize in ExtHostChatDebug._deserialize
  Event \u2014 the serializer sends it but the round trip was dropping
  it. Add the corresponding requestName field to the
  ChatDebugModelTurnEvent ext-host class so the assignment compiles.

* Cache Explorer: address Copilot review on #313620

Five fixes from Copilot's review:

- Rename INormalizedMessage.byteLength to charLength (text.length is
  UTF-16 code units, not bytes), and update all UI labels from 'B' to
  'chars' so the displayed unit matches what we actually measure.
  Touches the diff engine, the explorer view, and the unit tests.
- setSession now clears collapsedGroups and resets openComponents to
  the default expanded set, mirroring how Flow Chart resets its
  collapse state on session change. Prevents unbounded growth and
  cross-session collapse-state leaks.
- Rail rows are now keyboard accessible: each row is focusable
  (tabIndex=0), exposes role='button', aria-selected, and aria-label,
  and responds to Enter/Space. Adds a focus-visible outline.
- render() now uses a monotonically-increasing renderToken captured
  at the start of each call and re-checked after each await; an older
  render whose model-turn resolves come back late will no longer
  write into a DOM the newer render has already rebuilt.
- _reviveResolvedContent in mainThreadChatDebug now passes through
  maxInputTokens and maxOutputTokens, which were silently dropped.

Refs #313620

* Cache Explorer: address Councillor-Opus follow-up nits

Five fixes prompted by the council review:

- breakBytePos used to fall through to 'cumulative' (the right edge of
  the bar) when the diff's break index was outside the side's segment
  list \u2014 it now returns undefined, which the renderer already handles
  as 'no break marker for this side'. Prevents a logic mismatch
  between the diff and the segment list from being silently masked
  as a misleading 'cache broke at the end' marker.
- pickCacheRelevantRequestOptions drops the 'extra' catch-all. We
  now only forward an explicit allowlist of cache-keying body fields
  to OTel and the on-disk debug log. Keeps any future provider-
  specific body fields (auth tokens, API keys, personalization)
  from leaking through; new cache knobs must be added explicitly.
- Replace the local JSON-stringify based deepEqual helper in the
  view with the equals function from vs/base/common/objects, which
  is already used elsewhere in the workbench for value comparisons.
- Add a fast-fail comment to messagesEqual explaining why charLength
  stays even though it is implied by text equality.
- Document the trailing-context loop in renderInlineDiff and the
  silent selectedIndex clamp on session change so future readers
  don't think they're bugs. Expand the isLikelyCacheExpiration
  JSDoc to enumerate other invalidation causes the heuristic
  cannot distinguish.

* Cache Explorer: clarify stableStringify fallback intent

Document why stableStringify falls back to String(value) (circular
refs / BigInt) and why the diff engine deliberately does not take an
ILogService dependency to log such cases. The fallback produces a
stable but lossy representation that still surfaces as content drift
in the UI, so the failure mode is visible rather than silent.

* Cache Explorer: address Copilot review nits round 2

Four small fixes from the latest Copilot review pass:

- Update parseInputMessages JSDoc: was still describing charLength as
  'byte length' even though the field was renamed.
- Update diffPromptSignature comment: it claimed every position from
  the divergence onward is reported as non-identical, but the
  algorithm classifies each position independently. The first
  divergence is what breaks the cache; later identical positions are
  reported truthfully and the UI keys off the first break index.
- Rename the 'bytes' field on the local renderSignature segment type
  to 'chars' (and the breakBytePos helper to breakCharPos) so the
  source code matches what the user-visible labels already say.
- Drop the dead 'tools' entry from the openComponents Set seed in
  setSession and the field initializer; the diff pipeline only emits
  'system' and 'messages[N]' component names, so the 'tools' entry
  never matched and had no visible effect.
themes: improve quick pick focus contrast in 2026 themes (#307581)

Update the 2026 Light and 2026 Dark quick input focused row colors to meet non-text contrast requirements against the quick input background.

Also switch the focused row foreground and icon foreground to white so the stronger focus background remains legible.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use proper functions to get chat session types
This should be documented, not deprecated :)
Enable the toggle markdown preview command for agent diff views too
* Evict idle restored agent host sessions

Adds reference-counted resource subscribers to AgentService so the
agent-host can drop session state for restored sessions that nobody is
viewing.

Server side (src/vs/platform/agentHost):
- AgentService: new addSubscriber/removeSubscriber + per-resource
  refcount via _resourceSubscribers; on last release, run
  _maybeEvictIdleRestoredSession which drops state when the session
  is restored AND idle.
- AgentHostStateManager: new _restoredSessions set + isRestoredAndIdle.
- protocolServerHandler: wire subscribe/unsubscribe RPCs to the new
  refcount, and drain a client's subscriptions in transport.onClose.

Renderer side (src/vs/sessions/contrib/agentHost):
- baseAgentHostSessionsProvider: 30 s idle timer
  (_keepSessionStateAlive) before tearing down the per-session
  subscription, using the existing IAgentConnection refcount.

Tests: agentService, agentHostStateManager, protocolServerHandler,
localAgentHostSessionsProvider — all updated.

(Written by Copilot)

* Drop restored-vs-created distinction; evict any idle session

The previous implementation gated eviction behind an _restoredSessions
set, on the theory that in-process sessions might not yet be persisted
to the agent SDK. In practice, by the time AgentService.createSession
returns, provider.createSession has resolved — meaning the backend
agent owns the session and listSessions() will return it. So the gate
was guarding against a scenario that doesn't happen.

Remove the set, the predicate, and its tests. Eviction now keys solely
on the active-turn veto, which is the only correctness-critical guard.
The next subscribe rehydrates via the existing restoreSession path.

(Written by Copilot)

* Wire renderer subscribe through addSubscriber/removeSubscriber

Drop IAgentService.unsubscribe. It was a no-op on the agent host side,
which meant the electron renderer's subscriptions never decremented the
per-resource refcount — so the idle-session eviction added in this PR
only fired for the CLI/remote path.

The renderer now pairs every subscribe with addSubscriber and routes
the AgentSubscriptionManager unsubscribe callback to removeSubscriber.
The CLI/remote path is unchanged: protocolServerHandler still maps the
'unsubscribe' wire notification to removeSubscriber.

(Written by Copilot)

* Fold subscriber registration into IAgentService.subscribe

The subscribe/addSubscriber pairing was a footgun: callers had to
remember to call both, and forgetting addSubscriber meant the
per-resource refcount never tracked them, so idle eviction would never
fire. Now subscribe(resource, clientId) registers the subscriber as
part of the call.

Also rename removeSubscriber → unsubscribe so the cleanup half of the
pair has the obvious counterpart name (the previous unsubscribe was a
no-op and was removed earlier in this PR).

addSubscriber stays on the interface for the JSON-RPC handshake fast
path (initialize / reconnect serve snapshots out of the in-memory
state cache without going through the async subscribe()), with docs
narrowing it to that use case.

(Written by Copilot)

* fixes

Co-authored-by: Copilot <copilot@github.com>

* Reconnect: rehydrate evicted state via subscribe()

When all clients disconnect, _maybeEvictIdleSession drops the cached state. The reconnect path was calling getSnapshot() to re-attach prior subscriptions, which silently dropped any subscription whose state had been evicted — the client would think it had reconnected, but the server held no subscription and no state for that resource.

Have _handleReconnect call IAgentService.subscribe(uri, clientId) per prior subscription instead. subscribe() both registers the subscriber and restores state if missing, which is exactly what reconnect needs. Per-subscription failures are logged and skipped so one bad URI doesn't fail the whole reconnect.

Splits the handler into a sync part (installs the client object so any in-flight messages can find it) plus an async response promise. Updates the existing two reconnect tests to await the response, and adds a new test that asserts reconnect re-hydrates state evicted while the client was disconnected.

(Written by Copilot)

---------

Co-authored-by: Copilot <copilot@github.com>
Fix custom editor field documentation
fix: enable text selection in elicitation dialog markdown content
* Optin UX

Co-authored-by: Copilot <copilot@github.com>

* setting rename and updates

Co-authored-by: Copilot <copilot@github.com>

* cleanup

Co-authored-by: Copilot <copilot@github.com>

* few updates

Co-authored-by: Copilot <copilot@github.com>

* Feedback updates

Co-authored-by: Copilot <copilot@github.com>

* policy doc

* few updates

Co-authored-by: Copilot <copilot@github.com>

* minor update

* excludeRepo changes

Co-authored-by: Copilot <copilot@github.com>

* update policy name

Co-authored-by: Copilot <copilot@github.com>

* delete

Co-authored-by: Copilot <copilot@github.com>

* status UI

Co-authored-by: Copilot <copilot@github.com>

* delete and reindex updates

Co-authored-by: Copilot <copilot@github.com>

* UX and status updates

Co-authored-by: Copilot <copilot@github.com>

* policy update

* feedback updates

Co-authored-by: Copilot <copilot@github.com>

* telemetry update

Co-authored-by: Copilot <copilot@github.com>

---------

Co-authored-by: Copilot <copilot@github.com>
fix: resolve NoChangeError tool name interpolation and typo
@pull pull Bot locked and limited conversation to collaborators May 1, 2026
@pull pull Bot added the ⤵️ pull label May 1, 2026
@pull pull Bot merged commit eaa498e into KingDEV95:main May 1, 2026
10 of 24 checks passed
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