Wait for the intentional sample failure and final telemetry event instead of relying on a fixed wall-clock delay.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add Copilot cloud sandbox sessions over a live AHP relay
Copilot cloud sessions whose tasks run in a Mission Control sandbox (agent slug
`copilot-developer-cli`) are rendered by polling REST logs today, so they have no
slash commands and no steering.
Connect to the sandbox instead: `copilotd` runs inside it and speaks the Agent
Host Protocol, so once a relay is open the existing agent-host stack renders the
session natively — history, live output, steering and slash commands.
The sandbox is not directly addressable, so both sides connect outward to an
Azure Web PubSub relay brokered by Mission Control. Credentials are minted per
connection, and the user's GitHub token is forwarded only as a sealed envelope
the relay cannot read.
Everything runs in the renderer, so this also works in VS Code Web, where no
Copilot extension host is available. Mission Control is reached over the Copilot
API host: `api.github.com/agents/*` omits CORS headers on authenticated
responses, so a renderer fetch receives the reply and then discards it.
The AHP `initialize` now advertises every protocol version this client can
negotiate rather than only the newest, so a sandbox running an older `copilotd`
can negotiate down instead of being rejected.
Gated behind `chat.agentHost.cloudSandbox.enabled`, off by default.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR review feedback for cloud sandbox sessions
- Subscribe to host state by the backend URI. Session output, changesets and
last-turn changes still rebuilt the URI from the UI scheme, so an aliased
sandbox session subscribed to a URI the host's registry does not know.
- Apply the session-scheme alias on the live `SessionAdded` path too. Only the
refresh and persistence paths adopted, so a session created while connected
kept the host's scheme and never routed to a content provider.
- Report discovery completeness instead of a bare list. A failed or partial scan
read as "these sessions no longer exist", so a transient request failure could
tear down live providers and cancel the auth retry. Only a complete scan is
reconciled against.
- Cancel in-flight work when the feature is disabled. Discovery and connect ran
with `CancellationToken.None` and did not recheck enablement after their
awaits, so either could commit state — including a live relay — after teardown.
- Document why publish acks are not tracked, and why the feature is gated on a
setting: sandbox tasks carry a slug the Copilot extension's cloud provider does
not list today, but that is expected to change.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Reuse the shared Copilot API host constant
The cloud sandbox service declared its own copy of `https://api.githubcopilot.com`
alongside the one in `githubEndpoints.ts`. Export the existing constant and use it,
noting that it is distinct from `IGitHubEndpoints.apiBaseUri` (`api.github.com`).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* agentHost: Remove obsolete tool arguments metadata
Use the AHP toolInput field as the sole source of tool invocation input instead of duplicating serialized arguments in the provider-specific _meta bag. Remove the legacy producer, typed metadata slot, and obsolete tests.\n\n(Written by Copilot)\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* agentHost: Address tool input review feedback
Reject array-valued tool input when synthesizing structured SDK arguments and make the history assertion validate the raw AHP toolInput representation directly.\n\n(Written by Copilot)\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* sessions: Fix voice bridge test construction
Supply the sessions and chat widget service mocks required by NewChatVoiceTargetService after concurrent voice changes landed on main.\n\n(Written by Copilot)\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* telemetry: report ResizeObserver delivery context
The browser's ResizeObserver loop ErrorEvent contains only a generic message; it does not expose the skipped target or causative callback. Prefixing that warning with the most recently invoked DisposableResizeObserver therefore turned callback order into false causal attribution.
Record the wrapped observers that ran in the current rendering frame instead. Context is scoped per window, deduplicated, sorted for stable bucketing, and bounded to the canonical smallest eight names plus an overflow marker. The telemetry prefix explicitly calls these recent wrapped observers rather than an offending consumer.
Clear context at the next animation frame, after Blink dispatches any loop warning from the current rendering update. Unit coverage locks deduplication, canonical overflow, window isolation, and frame cleanup; a real Chromium component fixture deliberately triggers a self-resize loop and verifies context survives until the native warning.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: aacd276e-cf84-48bd-a2ab-6f6a4d4c3431
* telemetry: shorten ResizeObserver context prefix
ResizeObserverLoopContext already identifies the observer names as non-causal context, so remove the redundant
ecentWrappedObservers= label from the emitted error string.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: aacd276e-cf84-48bd-a2ab-6f6a4d4c3431
---------
Copilot-Session: aacd276e-cf84-48bd-a2ab-6f6a4d4c3431
Keep provider thinking and opaque continuation metadata across the Agent
Host OpenAI loopback boundary. Cache it by authenticated session and tool
call, then rehydrate the assistant turn when Copilot replays the call.
This preserves Gemini thought signatures, Claude signed and redacted
thinking blocks, and unsigned reasoning content used by compatible
providers such as DeepSeek and Kimi. Bound the cache per session and add
round-trip, ordering, coalescing, retry, and isolation coverage.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Address review feedback
- Resolve the type check child process on `close` rather than `exit`, so the
captured stdout/stderr is complete before it is inspected. Resolving on
`exit` could race with the pipes draining and truncate diagnostics.
- Use an explicit type predicate when filtering out unresolved symbols rather
than relying on inferred narrowing of `Symbol | undefined`.
- Drop the redundant second `disallowedTypes.includes()` in toViolations by
splitting the direct name match from the symbol id lookup.
- Fix a collapsed function signature and reword the pass 1 comment, which
described files matched by a rule that disallows nothing as "exempt" and
read as though skipped files were traversed.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
docs: fix minor typos and grammar in README
- Fixes spacing and indentation for the VS Code image HTML wrapper.
- Updates "Visual Studio Code's website" to "the Visual Studio Code website" for better readability.
- Changes "help us verify as they are checked in" to "help us verify them as they are checked in".
- Merges "code base" into the modern single-word form "codebase".
- Adds missing commas for introductory clauses (e.g., before "which creates a Docker volume" and "see the Code of Conduct FAQ").
xtab: fix: clamp tagged patch range to document
Tagged prompt content can add lines beyond the source document. Clamp the CustomDiffPatch pseudo-window to the source range to avoid an unexpected failure.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 62cca64b-0581-410e-ab89-034e20f02ca7
* Validate workspace trust for notebook links
Route notebook webview resource links through the editor service trust gate while preserving selections and editor group targeting.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Preserve notebook link opener behavior
Keep custom URI, folder, fragment selection, and existing editor handling intact while validating trust for out-of-workspace resources.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix notebook link opener options
Rely on the opener URI fragment for selections instead of passing text editor options through the generic opener API.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The check took ~257s and would sometimes run out of memory. Two separate
bottlenecks were responsible.
layersChecker.ts built a full in-process ts.Program (15s, 2GB) and then
asked the type checker for a symbol at every property access in the
program, ~2.5M identifiers. Each lookup materialized symbol and type
objects, pushing the heap past 3.6GB, and the alias/containment chain was
then walked per symbol.
It now runs on the TS7 native API that the repo already vendors for
build/lib/tsgo.ts:
- updateSnapshot replaces createProgram, so the program lives in the
compiler rather than the V8 heap
- the disallowed types are resolved to symbols once up front, turning the
per-reference check into a local id lookup
- symbol lookups are batched instead of issued one node at a time
- property accesses are prefiltered by member name, so only those that
could name a member of a disallowed type are resolved at all
That brings it from 48.6s and 3.8GB peak to 11.7s and 0.8GB peak.
The six tsconfig.<layer>.json projects were type checked serially with
TS6, which accounted for the remaining ~208s. They move to the native
compiler and run in parallel in the new layersTypeCheck.ts, taking ~11s.
Concurrency is derived from free memory (the largest project peaks at
~3.5GB) so a memory-constrained machine falls back towards serial
execution rather than swapping.
Verified to report byte-identical violations, including line and column,
against a seeded set covering direct references, import aliases,
inherited members, nested member access and ipcMain.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Initial plan
* Only report 'Downloading' model status on an actual cache miss
Co-authored-by: meganrogge <29464607+meganrogge@users.noreply.github.com>
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: meganrogge <29464607+meganrogge@users.noreply.github.com>
* Initial plan
* Disconnect stale voice session when a fresh new-session composer takes over
Co-authored-by: meganrogge <29464607+meganrogge@users.noreply.github.com>
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+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>
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: meganrogge <29464607+meganrogge@users.noreply.github.com>
Co-authored-by: Megan Rogge <merogge@microsoft.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* agentHost: Sync session read state across all connected clients
Read/unread for an Agent Host session is now a single host-owned fact that
every connected client agrees on.
The host had two parallel representations: the `SessionStatus.IsRead` bit
(persisted under the `isRead` session-database key) and separate
`isRead`/`isArchived` booleans on the local `IAgentSessionMetadata` IPC
shape. Different paths read different ones and they could disagree, so the
booleans are gone and `status` is the sole carrier. The database values now
fold into `status` once, in `AgentService.listSessions()`/`restoreSession()`,
and the redundant re-folds in `protocolServerHandler` and
`remoteAgentHostProtocolClient` are removed.
Persistence of both flags moves onto the existing `onDidEmitEnvelope`
observer, which sees client- and server-dispatched actions alike and skips
rejected envelopes. Previously each dispatch path wrote the database itself,
so any new server-side dispatch would silently skip persistence.
The editor window had its own disconnected read model while the agent window
was already wired to the host, so the two windows disagreed. It now shares
the same state through a bridge mirroring the archive one:
`IChatSessionItem.isRead`, `IChatSessionItemController.setChatSessionItemRead`,
and `canSetChatSessionItemRead`/`setChatSessionItemRead` on
`IChatSessionsService`. `AgentSessionsModel` delegates to it when the
provider owns read state; its local timestamp heuristics now apply only to
providers that do not. `migrateReadStateToProvider` performs a one-time,
additive hand-off of existing local read state so sessions do not resurface
as unread on upgrade, deferring until the provider has actually reported so
a stale startup cache cannot consume the one-shot flag.
Neither the Copilot SDK nor Claude/Codex track read state, and the host
drives it from provider-neutral turn signals, so this works identically for
all three agents.
(Written by Copilot)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* agentHost: Trim read-state comments and docs
Cut restatement and over-explanation from the comments added in the previous
commit, keeping only what isn't already obvious from the code. Condenses the
SESSIONS_LIST.md read/unread section to the user-visible behaviour rather
than the implementation detail behind it.
(Written by Copilot)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* agentHost: Only report sessions known to be unread
Address PR feedback on #327950.
Folding the read flag into `status` accidentally widened the `unread`
semantics in the session server tools: `isSessionStatusRead(undefined)` is
`false`, so a session with no status was treated as unread. The previous
code required an explicit `isRead === false`. This is reachable and common —
`ClaudeSessionMetadataStore.project()` returns no `status`, so every cold
Claude session would have matched an `unread: true` listing and serialized
as unread. `sessionIsUnread` now requires a known status, restoring the
original semantics.
Also hoists the duplicated read-timestamp comparison out of `isRead` and
`migrateReadStateToProvider` into `localReadDateCoversActivity`, so the
grace window and baseline fallback live in one place.
(Written by Copilot)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* agentHost: Don't infer read state from a synthesized status
Address GPT-5.6 review feedback on #327950.
Neither `CopilotAgent.listSessions` nor `ClaudeSessionMetadataStore.project`
projects a `status`, so every cold session that has never been marked read or
archived reaches clients without one. `SessionSummary.status` is required, so
the session-list store synthesized `Idle` — and the editor window then read the
absent `IsRead` bit off that synthesized value and reported the session as
unread, while the agents window treats an absent status as read. The two
windows disagreed on the most common cold session, which is exactly what this
change set out to fix.
The list entry now records whether the status came from the host, and the
controller leaves `IChatSessionItem.isRead` unset when it did not. That keeps
all three surfaces consistent (agents window, editor window, and the
`sessionIsUnread` server-tool helper) and stops the migration from consuming
its one-shot flag for a session whose real state was never reported. The flag
is resolved as soon as a summary carries a status, or when a client mutation
establishes one.
Also moves the migration ledger from workspace to application scope. The state
it hands off to is host-global, so a second workspace that can see the same
session — an empty window lists them all — would otherwise migrate it again and
re-promote a session the user had deliberately marked unread elsewhere.
(Written by Copilot)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The renderer minted a client-side `request_<uuid>` id for turns it did not
dispatch itself, so `/btw` sent the agent host a turn id it had never seen and
createChat failed with "side chat source turn ... not found". The same broken
anchor affected selection side chats, Ask in Side Chat, and forking.
Two paths dropped the provider's turn id: the active turn pushed into history
on reconnect, and server-initiated turns surfaced via onDidStartServerRequest.
Both now carry it. The id is required rather than optional so a future caller
cannot silently omit it and fall back to a client-generated id.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The interim decoration was applied correctly but had no visual effect: the
`.monaco-editor .dictation-interim-processing` rule ties on specificity with
Monaco's syntax-token rules (`.monaco-editor .mtkN`), which are injected into the
document after this stylesheet and therefore won, painting the provisional text
in the normal foreground color.
The shimmer never hit this because it colored text through -webkit-text-fill-color
and background-clip rather than `color`, which bypasses the tie entirely.
Qualify the selector with .view-line and the element name so it outranks the
token rules regardless of injection order. Verified in a running Code OSS build:
a span carrying both `mtk1` and the decoration class now computes to the
placeholder color instead of the editor foreground.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Splitting on the recognizer's finalized prefix looked right in principle, but
streaming backends endpoint segments almost as fast as they are spoken. Styling
only the uncommitted tail therefore left nearly every word solid on arrival, so
the provisional state was never visible in practice - dictation appeared to jump
straight from empty to the finished transcript.
Render the whole not-yet-final transcript in the placeholder color instead, and
clear it when dictation ends. This matches the pre-existing visual behavior
minus the animation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Agent Host: Share agent catalog across session adapters
Avoid one root-state listener per cached session by publishing the agent catalog once from the provider. Preserve late capability hydration and cover listener scaling with 200 adapters.\n\n(Written by Copilot)\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Agent Host: Address shared catalog review feedback
Use public listener lifecycle hooks in the regression test, clear derived root state on errors and disconnects, and precompute the provider capability lookup.
(Written by Copilot)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.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: Emit chat.toolApproval telemetry
Emit the workbench-compatible tool-approval event from the agent host on the standard telemetry channel, correlating SDK permission events with each tool invocation. Fires once per tool call at either permission.completed (denied) or tool.execution_start (approved / no permission requested).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* agentHost: Wait for tool permission outcome
Defer chat.toolApproval emission until permission.completed when a permission lifecycle exists. Tool calls without permission requests now emit at completion with no inferred auto-approval reason, preventing premature and contradictory rows.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Trims only the end of the selected text when building the side-chat
snippet. The trailing newline browsers append when a line selection
spills into the next block is still dropped, but a selection that
starts mid-indentation (a nested list item, a quoted line) no longer
silently loses the whitespace the user actually selected.
Follow-up to #327931, addressing Copilot review feedback.
(Commit message generated by Copilot)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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>