While dictating, the in-progress transcript animated a gradient shimmer over
the words the recognizer had not committed yet. Render that tail in the input
placeholder color instead, so text simply turns solid as the recognizer commits
it.
Because nothing animates any more, the heuristics that existed only to stop the
shimmer looping during a pause are gone: the interim diff, the word-boundary
backoff, and the 700ms idle-settle timer. The split is now driven purely by the
finalized prefix the recognizer reports, so a word can no longer flip from solid
back to placeholder when it is revised.
The terminal voice surface gets the same treatment so all three dictation
surfaces match.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Bring back terminal auto approval edit settings.json
* Scope terminal auto-approve actions to rule-eligible Agent Host Copilot confirmations
* Tighten auto-approve rule eligibility and evaluate persisted rules only
* Add gate coverage for canFutureRuleSuppressPrompt
* Extend auto-approve rule test coverage and drop a dead guard
* Preserve focus when chat confirmation buttons update
* Carlify terminal auto-approve rule eligibility
* agentHost: Separate local resource identity
Keep trusted local resource access distinct from remote host addresses and give a remote host named local a collision-free authority.\n\n(Written by Copilot)\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* agentHost: Type local resource identity as unique symbol
Make the trusted local sentinel explicitly narrow for control-flow checks.\n\n(Written by Copilot)\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Scrub the account name only where it identifies a user
Snapshot and capture normalization ended with an unanchored
`replaceAll(userName, '${user}')`. With the GitHub Actions Linux account
name `runner` — an ordinary English word — captured text such as
`the runner completed` was rewritten to `the ${user} completed`.
That is worse than cosmetic. It only misfires on platforms whose account
name happens to be a common word, so the same recording normalizes
differently on macOS and Linux CI, which is exactly the cross-platform
mismatch this normalization exists to prevent.
Replace only the two positions where the name genuinely identifies a
user: after a path separator (including the escaped form found in
embedded JSON), and the owner/group columns of an `ls -l` listing. The
listing case is load-bearing — the committed subagent capture records the
account name there, outside any path — so it cannot simply be dropped.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Enable the git-branches config test on Windows
The test was disabled on Windows because its temporary git repository
could not be deleted, which failed suite teardown even though every
assertion passed. Its assertions were always platform-independent.
Two independent causes, both now addressed:
- Read-only `.git/objects` files. Fixed by clearing read-only attributes
before retrying removal, which also unblocked `inspects git status`.
- Background `git gc` holding handles under `.git` after the test
finishes. `initTestGitRepo` now sets `gc.auto 0`; these repositories
never create enough objects to need it.
Collapse the duplicated init/identity setup in three suites into
`initTestGitRepo` so the gc setting cannot be forgotten by the next test
that needs a repository.
Whether this holds can only be confirmed on a Windows run; the remaining
risk is a handle neither mechanism covers.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Enable the worktree working-directory test on Windows
The comment claiming this test could not be made portable was wrong on
two of its three counts:
- The path assertions use `URI.fsPath`, which is platform-native, and the
expected value is derived at runtime from the host's own `sessionAdded`
notification rather than hardcoded as a POSIX path.
- The recorded `pwd` is only reached when the provider routes commands
through the host terminal tool, which on Windows is PowerShell, where
`pwd` is an alias for `Get-Location`.
The third — that pinning a command stops the turn on an unanswered
permission prompt — was observed in a different test. This one already
handles confirmation, dispatching `ChatToolCallConfirmed` when
`toolCallReady` arrives unconfirmed.
Drop `pwd` from the record-time command blocklist for the same reason,
which empties `POSIX_COMMAND_EXCEPTIONS`; a recording now passes the check
without an opt-out.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Record why the worktree test keeps `pwd` rather than a pinned node command
Tried replacing `pwd` with `node -e "console.log(process.cwd())"` on both
of the test's turns. Both stalled on `session/inputNeededSet`: providers
auto-approve `pwd` as a safe read-only command, and an arbitrary `node -e`
invocation is not on that list, so pinning turns a silent tool call into
one that waits for a confirmation this test's flow does not answer — even
on the turn that does dispatch `ChatToolCallConfirmed`.
`pwd` is portable regardless, since PowerShell defines it as an alias for
`Get-Location`, so the test still runs on Windows.
Correct the stale comment, which still claimed the assertions were
POSIX-shaped, and record the general point: pinning a command changes its
permission posture, so prefer steering to a file tool where one exists.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Pin the worktree test command and approve tool calls in a loop
Answers why pinning `node -e` failed here when it works in every other
ported test: those all go through `driveTurnToCompletion`, which confirms
each unconfirmed `chat/toolCallReady` as it arrives. This test drives its
turn by hand, and its host-terminal branch approved exactly once while its
SDK-shell branch already used `startBackgroundApprovalLoop`.
A pinned command is not on the provider's auto-approve list the way `pwd`
is, so it adds an approval round-trip. With single-shot approval a later
request stays pending and the turn stalls on `session/inputNeededSet`,
which reads as a hang rather than a permission problem.
Both branches now use the shared loop, and the command is pinned like
everywhere else. Verified by re-recording both providers.
This also removes the last special case: no test now depends on a command
being auto-approved, and `POSIX_COMMAND_EXCEPTIONS` stays empty.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Revert the worktree test to non-Windows after CI
Windows CI failed it for two reasons, neither about command portability,
and both specific to this test's output assertions:
- The expected path comes from `os.tmpdir()`, which on Windows CI returns
an 8.3 short form (`C:\Users\CLOUDT~1\...`) while the shell reports the
long form, so the `includes()` assertion can never match and the wait
times out. Same class of mismatch as `/var` versus `/private/var` on
macOS, which snapshot normalization already special-cases.
- The Copilot branch waits for a `chat/toolCallContentChanged` carrying a
terminal resource, and on Windows that notification never arrives even
though the tool call starts, is confirmed, and completes.
Keep the pinned command and the shared approval loop from the previous
commit — both are correct regardless of platform — and record what CI
showed so the next attempt starts from evidence rather than a fresh guess.
Reworking this needs `realpathSync.native` on both sides of the path
comparison, and the missing terminal content understood first; neither is
reproducible without a Windows machine.
`session configuration resolves and completes git branches` passed on
Windows in the same run, so the git lock fixes hold.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* agentHost: address E2E review feedback (Written by Copilot)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* agentHost: assert the recorded model request on replay
Response selection stays ordinal, but every replayed turn now compares the
live model request against the one recorded in the capture, through a
projection applied symmetrically to both sides. Captures keep their existing
shape and stay readable.
The request body is the host's own product - prompt assembly, retained
history, truncation, attachment marshalling, and tool-result hand-back - and
none of it was checked before, so a regression replayed green and became the
new expected value at the next re-record.
Asserts host-authored structure (roles and ordering, retained history, system
prompt presence, text and attachment content, tool names/inputs, tool_use_id
wiring) and elides what replay cannot reproduce or what is environment-derived
(tool_result payloads, run-time identifiers, reasoning blocks, the model id).
Switching this on found seven stale captures: four whose prompt text had been
edited without re-recording, one holding a one-off ordering of parallel
tool_result blocks, and one Codex capture still carrying the pre-pinned pwd
prompt - all re-recorded here. The seventh, Claude's side-chat capture, cannot
be refreshed because recording it hits the known provider-context fork defect,
so it is listed as an exception and documented in KNOWN_ISSUES.
(Written by Copilot)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* agentHost: compare paths by filename, not by directory spelling
Windows CI failed three model-request assertions. None was a host regression:
each was the same file addressed differently than the macOS-recorded capture.
Three separate spellings were involved - an unsubstituted \${workdir}, an
unsubstituted \${homedir}, and a backslash separator - which is the point: a
directory has too many per-machine spellings (separator, drive letter, 8.3
short names, /var vs /private/var, and whether the recorder substituted its
placeholder at all) to be compared literally across platforms.
The projection now collapses a path to \${path}/<basename>. Which file the
host referenced stays asserted; where it lives does not. A different filename
is still a mismatch.
Also fixes a real gap in the recorder: \`homeDir\` was normalized only in its
raw form, while \`workDir\` also had a JSON-escaped variant, so a Windows home
directory embedded in a JSON body was never substituted.
(Written by Copilot)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* agentHost: elide filesystem paths from the model-request projection
The remaining Windows failure was a recorded bare `${workdir}` (the workspace
directory itself) against a live raw directory path. Keeping the basename
cannot reconcile those two, because the live basename is the temp directory's
own name.
Paths are now elided entirely rather than reduced to their filename. A path has
too many per-machine spellings to compare literally - separator, drive letter,
8.3 short names, /var vs /private/var, whether the recorder substituted its
placeholder, and whether the value is a file or the directory itself - and two
rounds of Windows CI failures came from exactly those, none a real regression.
This is a deliberate narrowing: the projection no longer asserts which file a
prompt referenced. That is a weaker oracle than it looks, because the tests
that care assert the filesystem effect directly. The instruction wrapped around
the path is still compared.
(Written by Copilot)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* agentHost: compare model-request projections with stable key ordering
A tool call's `input` is JSON the model produced, and its key order is not
guaranteed to survive a re-record or a YAML round-trip. Comparing raw
`JSON.stringify` output reported two semantically identical requests as a
mismatch, which reads as a host regression and is especially confusing because
the two printed values look equivalent.
Both the comparison and the failure message now use a canonical form with keys
sorted, so what is printed is what was compared. Key-order tolerance does not
extend to values: a changed input is still a mismatch.
(Written by Copilot)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix duplicate symlink decorations in explorer
The explorer decorations provider was registered per ExplorerView instance.
Since DecorationsService merges all providers' letters into a single badge,
each registration contributed another arrow. In the classic workbench there
is only ever one ExplorerView so this never surfaced, but in the Agents
window the Files view is gated on session context keys and re-created on
session switches, leaving multiple live views and therefore multiple badges.
Register the provider once from ExplorerService instead, which owns the
window wide explorer model the decorations are derived from.
Fixes#324389
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add 1px margin-bottom to workspace picker container
The new-session-workspace-picker-container should have a 1px bottom margin
to provide proper spacing between the workspace picker and the chat input.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Fix agent host diff file paths
Legacy session-db content URIs encode the blob location in the URI path,
so the original side of a diff showed a '.../before/<file>' label and was
flagged as a rename. Canonicalize them into the current layout at the
single consuming choke point, normalizeFileEdit.
Agent host URIs also rendered as '/c:/Code/...' next to 'C:\Code\...' for
the same file, because the label formatter had no drive letter
normalization and a hardcoded posix separator. Register an OS-aware
formatter for the in-process host and normalize drive letters everywhere.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Make agent host path handling operating system independent
canonicalizeSessionDbUri rebuilt the path with URI.file, whose backslash
handling follows the client OS, so a legacy Windows session canonicalized
incorrectly on a POSIX client. Take the path from the file URI the host
already resolved instead.
Drop normalizeDriveLetter from the fallback label formatter: '/c:/repo/a.ts'
is a valid POSIX path too, so rewriting it as 'C:/repo/a.ts' would name a
different resource. Drive normalization now only happens for hosts whose
operating system is known.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(agentHost): add multi-root support for Claude agent sessions
- Introduced `AgentHostClaudeMultiRootEnabledSettingId` to enable multi-root capabilities for Claude agent-host sessions.
- Updated configuration schema to include `multiRootEnabled` setting for Claude agent.
- Enhanced `ClaudeAgent` to advertise `multipleWorkingDirectories` capability based on the new setting.
- Modified session handling to support additional working directories, allowing sessions to span multiple directories.
- Implemented persistence of additional directories in session metadata for cold resumes and forks.
- Added tests to verify the correct behavior of multi-root functionality, including session creation, metadata handling, and SDK options projection.
* test: add test for session creation with fork that inherits working directories
* agents: restore message timestamps from persisted agent transcripts
Chat request/response timestamps disappeared after restarting the client because the Copilot and Codex history replay mappers dropped turn timing when rebuilding sessions from persisted transcripts.
Restore Turn.startedAt/duration in both mappers: the Copilot mapper now reads the ISO timestamp from the SDK session event envelopes, and the Codex mapper derives timing from the persisted thread turns.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* agents: bound replayed turn duration by the turn's own events
Address PR review feedback: track the last event timestamp per turn builder instead of using the globally previous/current envelope timestamp, so events outside the turn (for example an ignored notification long after assistant.turn_end) no longer inflate its duration. Also shorten the new helper comments to one line.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: signature help active overload not updating
- Active overload now updates when typed arguments narrow the overload set
* fix: replace findIndex with direct index lookup in getActiveSignature
- Instead of searching signatures by label to get an index and comparing
it to info.selectedItemIndex, look up signatures[info.selectedItemIndex]
directly so both sides of the comparison use the same index source
* test: add unit tests for #268728 overload fix
- Extract getActiveSignature as an exported function so unit
tests can import it without the extension host
- Add BEFORE suite documenting the original bug: on retrigger,
old code returned the stale overload index even after
TypeScript updated selectedItemIndex (e.g. after a string
argument narrows the overload set on the comma trigger)
- Add AFTER suite verifying the fix: retrigger now honours
TypeScript's updated selectedItemIndex; the BUG test case
that returned 0 now correctly returns 1
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: address Copilot review on #268728
- Rename exported helper to computeActiveSignatureIndex to
avoid identifier collision with the private method
- Stop returning activeSignatureHelp.activeSignature: that
index refers to the previous signature list, which is stale
if the list reorders between invocations; always use
tsSelectedItemIndex instead
- Add regression test for the reordering case: verifies that
when the list reorders and TS still selects the same overload
by label, the current index is returned, not the stale one
* fix: address Copilot review on #268728
- Simplify computeActiveSignatureIndex to only accept
tsSelectedItemIndex; the context and signatures params
were unused and made the API misleading
- Add @internal JSDoc to signal the
computeActiveSignatureIndex export is for unit
testing only, not public API
- Replace assertion-based BEFORE suite with block comment
documenting the original bug; asserting known-wrong
behaviour institutionalizes incorrect expectations
* fix: simplify active overload selection
- Replace the label-matching retrigger guard with result.activeSignature = info.selectedItemIndex;
the guard became a no-op after the existingIndex === selectedItemIndex fix and its removal is the correct minimal change
- Export TypeScriptSignatureHelpProvider as _TypeScriptSignatureHelpProvider
(VS Code underscore-prefix convention for test-only exports)
- Add unit tests via mock ITypeScriptServiceClient: documents the old buggy guard behavior
and verifies the fix — the FIX test would fail if the label-matching guard were reintroduced
* fix: address Copilot unit test review comments
- Move CancellationTokenSource into setup/teardown so it is properly disposed after each test
- Add success: true and message: '' to mock response to match the protocol shape
- Add @internal test-only export JSDoc to _TypeScriptSignatureHelpProvider to make the export intent explicit
* Fix signature help overload selection
Track TypeScript-selected and user-selected overloads separately so retriggers follow updated recommendations without resetting manual selections.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Dmitriy Vasyura <dmitriv@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* added classification comments for response events
* Classify forwarded Copilot identifiers as pseudonymous
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* agentHost: Emit toolCallDetails telemetry
Preserve the local per-turn tool-call aggregate on the Microsoft telemetry channel while retaining the restricted peer events. Include Agent Host correlation fields and report successful, cancelled, and failed turns once.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* agentHost: Report tool-call details before steering
Reuse the normal turn completion path when steering preempts a turn so its telemetry aggregate is emitted before state resets.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Gemini BYOK function-declaration converter assumed a JSON Schema
`type` was always a single string. When a tool parameter used a nullable
union such as `{ "type": ["string", "null"] }`, the array was stringified
into `"string,null"` and rejected with `Unsupported type: string,null`.
Through the Agent Host this surfaced as an HTTP 502 that the Copilot SDK
retried five times before failing the turn. Because the failure lives in
the shared converter, regular editor BYOK is susceptible to the same
schema shape.
Normalize nullable unions to Gemini's `nullable: true`, map multi-type
unions to `anyOf`, and handle nullable array items and anyOf/oneOf that
contain `null`. Add regression tests for these cases.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: remove all manual folding ranges when selection is empty
When editor.removeManualFoldingRanges is invoked with an empty selection
(just a cursor, no text selected), remove all manual folding ranges in
the document instead of only removing ranges intersecting the cursor
line. This makes it easy to clear broken manual folding ranges without
having to select all text first.
When text is explicitly selected, the existing behavior of only removing
manual ranges intersecting the selection is preserved.
Closes#266597
* fix: combine innermost removal with full-range fallback
When cursor is on a manual folding range, remove only the innermost one.
When cursor is NOT on any manual range, remove all manual ranges.
This addresses both #266597 and #212599 in a single approach.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: move cursor-aware logic into removeManualRanges
Address review feedback: move the "find innermost manual range at cursor,
or remove all" logic from RemoveFoldRangeFromSelectionAction.invoke()
into FoldingModel.removeManualRanges(), since that action is the only
caller. The action now simply passes raw selection ranges as before.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add tests for removeManualRanges cursor behavior
Tests cover three scenarios:
- Cursor on a manual range removes only the innermost manual range
- Cursor not on any manual range removes all manual ranges
- Selection range removes only intersecting manual ranges
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix manual folding range removal
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test manual folding range edge cases
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Martin Aeschlimann <martinae@microsoft.com>
Co-authored-by: Dmitriy Vasyura <dmitriv@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: send chat image attachments when signed out of GitHub
Image attachments were silently dropped for signed-out users even when a
vision-capable BYOK/local model was configured. The panel Image prompt
element gated image inclusion on
`copilotToken?.isEditorPreviewFeaturesEnabled()`, which is `undefined`
when there is no Copilot token (i.e. not signed in), so the negation
evaluated truthy and the image was omitted before the request was sent.
`isEditorPreviewFeaturesEnabled()` represents an org policy that is only
meaningful when a Copilot token exists; a missing token should not block
the feature. Default the check to enabled when the token is absent, using
the same `?? true` pattern already used in
claudeChatSessionContentProvider. This preserves the org-policy behavior
(explicit `editor_preview_features=0` still omits images) while letting
signed-out / BYOK users send images to vision-capable models.
Applied to both HistoricalImage and Image render paths, and added unit
tests covering signed-out, signed-in, org-policy-disabled, and
non-vision-model cases.
Fixes#323854
Co-authored-by: SamirSaji <samirsaji13@gmail.com>
* fix: report accurate omission reason for policy-blocked images
Address review feedback: when a vision-capable model has images omitted
because org policy disables editor preview features, the omitted-reference
status previously said the model "does not support images", which is
misleading. Set the status description based on the actual cause
(lack of vision vs. org policy). The catch/error path keeps the generic
message.
Co-authored-by: SamirSaji <samirsaji13@gmail.com>
---------
Co-authored-by: SamirSaji <samirsaji13@gmail.com>
Co-authored-by: Dmitriy Vasyura <dmitriv@microsoft.com>
* fix(server): propagate --enable-proposed-api in serve-web
The `code serve-web` CLI accepts `--enable-proposed-api EXTENSION_ID`
but never propagated the allowlist to the workbench environment service
or the server-side extension scanner. As a result, extensions declaring
`enabledApiProposals` in their package.json had their proposals wiped
at runtime by ExtensionsProposedApi and failed to activate.
This wires the flag through two paths so it matches desktop behavior:
1. Server scanner: extend IProductService.extensionsEnabledWithApiProposalVersion
in setupServerServices so the node extension scanner keeps the manifest's
enabledApiProposals when --enable-proposed-api is passed.
2. Workbench env service: add a new `enabledExtensionProposedApi` field to
IWorkbenchConstructionOptions, populate it from the CLI args in
webClientServer, and surface it via BrowserWorkbenchEnvironmentService
.extensionEnabledProposedApi so the runtime allowlist in
ExtensionsProposedApi matches the requested IDs.
Fixes#228781
* cli: forward --enable-proposed-api to serve-web subprocess
The Rust CLI parses --enable-proposed-api as a global EditorOptions flag
but was not forwarding it to the node server subprocess spawned by
serve-web. As a result, the flag never reached the server's argv parser
and the server-side plumbing in serverServices.ts and webClientServer.ts
had no input to act on.
Capture the flag in ConnectionManager from CommandContext and pass it
through StartArgs, then append --enable-proposed-api=<id> for each
requested extension ID when spawning the server process.
* fix(server): update proposed API propagation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Martin Aeschlimann <martinae@microsoft.com>
Co-authored-by: Dmitriy Vasyura <dmitriv@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove the minimum-height filler from the latest chat response and clean up the related layout state, option plumbing, CSS, and component fixture.\n\n(Written by Copilot)\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>