sessions: remove session metadata setting
Make the new session tabs and input metadata presentation unconditional, and remove the retired header/tab fallback paths and fixtures.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds `isExternal` to every session-scoped telemetry event, adds
`pullRequestCount` / `pullRequestStatus` to the session summary event,
and stops the `agents/requestSent` session counts from counting the
session the request was sent to.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Batch and reuse DOM measurements across hot UI paths, replace per-mousemove geometry reads with element boundary events, and move terminal gutter alignment into CSS.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Opening a session spans the sessions list, the chat model load, an AHP
round trip and the agent host's database reads before anything renders,
but the timeline had large unlogged gaps. Add trace markers along that
whole path - for both the Agents window list and the workbench chat
sessions list - carrying the session resource so a single open can be
followed through the log.
Markers were only added where none existed; the already-instrumented
ChatWidgetService.openSession and ChatViewPane.loadSession are left
alone.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Agent Host changes for osortega/agents/review-chatfindmodel-widget-editor
* chat: index only the text a response renders in Find
Find counted matches that had no DOM node to land on, so the result count
overstated the total and navigation walked past positions it could never
reach. Three separate causes, each fixed where the text is indexed rather
than compensated for during navigation:
- Markdown link targets were indexed. `renderAsPlaintext` emitted a list
item's raw source instead of parsing its tokens, so a link kept its
target: a response listing its edits as `[src/](/some/path)` indexed the
path, of which only the label renders. Adds an opt-in
`parseListItemTokens` so the 90+ existing callers are unaffected.
- Filtered responses were indexed. The renderer drops the references slot,
the body and the citations for a filtered response, keeping only the
error message, which also shifted every predicted part index for the row.
- Parts merged into one block were fused. Plaintext is trimmed per part and
then concatenated, so `See ` + `foo.ts` + ` for details` was indexed as
`Seefoo.tsfor details`, hiding text that is plainly on screen.
Removes `dropActiveMatch`, which existed to correct the count after the
fact and made the total change as the user navigated.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* chat: name the plaintext option for what it guarantees
`parseListItemTokens` described marked's data model rather than the
output, and named only the case that surfaced the bug: the option also
reduces bold, emphasis and code spans, and swaps the block-level text
renderer. `omitMarkdownSyntax` states what the caller gets, so a future
leak fixed in another renderer folds in without a second option.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* chat: address Find review feedback
- Only schedule a debounced search when the query or options differ from
what the model last searched. An option toggle reaches `_onInputChanged`
after the base state listener has already run `findFirst`, so the second,
identical search left Enter flushing it instead of advancing a match.
- Keep a segment's *last* `limit` matches. The per-segment cap was applied
before the array was reversed, so a single over-limit segment retained its
oldest occurrences and dropped the newest ones navigation reaches first.
- Yield once before the result count snapshots state. `FindInput.onDidChange`
fires before the `onInput` handler that schedules the search, so the waiter
saw nothing pending and reported the previous query's matches.
- Condense multi-line method-body comments to one line.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* chat: separate a nested list from the item holding it
Verified `renderAsPlaintext` against the vendored marked and found two
things wrong with the list-item change.
Parsing a tight item's tokens with `top = false` skips the paragraph
treatment that gives block content a boundary, so an item holding a nested
list ran straight into it: `- outer\n - inner [link](/t)` produced
`outerinner link`. Parsing as top-level restores the break and also drops a
redundant blank line from loose lists.
The two new assertions expected a single newline between list items where
marked emits two; `renderer.list` joins items that already end in one.
Default output is unchanged in every case, including the pre-existing
plaintext suite.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* pet: add achievements and accessory rewards
Add persistent cross-window pet achievements with six enabled rewards, a standalone collection modal, account badges, and semantic unlock triggers. Add the body-owned accessory rig and atlases, unlock star and New state, accessibility help, fixtures, and tests while retaining disabled rewards for later re-enablement.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5c8a4f1e-3bb0-4d6f-a3ed-249fbcd4ce15
* pet: address achievement review feedback
Defer customization observation until the pet is enabled, detect newly installed MCP servers independently of enablement, and fully clear legacy fork state on reset. Rename the Crown persistence ID and use contrast-paired badge colors for the New affordance.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5c8a4f1e-3bb0-4d6f-a3ed-249fbcd4ce15
* pet: update model and skill rewards
Reward changing the model picker selection with the Construction Hard Hat, and reward adding a custom skill with the Crown. Keep the instructions achievement and Sailor Hat disabled for future use.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5c8a4f1e-3bb0-4d6f-a3ed-249fbcd4ce15
* pet: fix component fixture asset loading
Serve pet fixture media from the source tree used by both Vite and the CI rspack server, remove the intentionally empty screenshot variant, and approve the new blocking fixture snapshots.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5c8a4f1e-3bb0-4d6f-a3ed-249fbcd4ce15
* pet: accept component fixture screenshots
Record the authoritative Linux CI hashes for the new blocking pet achievement and accessory fixtures.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5c8a4f1e-3bb0-4d6f-a3ed-249fbcd4ce15
* pet: restore fake timers within unlock test
Avoid leaving the renderer test clock installed after the unlock-state interaction test so later notebook and notification suites can advance timers normally.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5c8a4f1e-3bb0-4d6f-a3ed-249fbcd4ce15
* remove unused achievements for now
* pet: remove unrelated branch changes
Restore server command, session artifact, and chat pill files to current main after they were accidentally included with the dormant achievement cleanup.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5c8a4f1e-3bb0-4d6f-a3ed-249fbcd4ce15
---------
Copilot-Session: 5c8a4f1e-3bb0-4d6f-a3ed-249fbcd4ce15
* sessions: keep ignored blocked sessions ignored
Ignoring a blocked session (e.g. "Ignore CI Failure") only held until the
user opened another session or the new-session view, after which the title
bar surfaced the very same block again. Two independent causes:
- `BlockedSessionsIndicatorModel` - the sole owner of the acknowledged
occurrences - was created by `SessionsTitleBarWidget`, a command center
action view item. The command center refreshes when `isNewChatSession`
changes, disposing and re-creating its view items, which discarded every
acknowledgement. Ownership moves to `SessionsTitleBarContribution`, which
outlives those rebuilds and now also owns `SessionActionFeedback`.
- `BlockedSessions` held its ref-counted GitHub pull request and CI model
references on the reader's store, which is disposed *before* each
recompute. Every session change therefore dropped the last reference,
disposed the shared models and re-created empty ones, so the session
briefly left the blocked set - and the cleanup autorun read that gap as
"no longer blocked" and released the acknowledgement. Those references
move to the reader's `delayedStore`, and a failing-CI acknowledgement
(keyed by the failing commit) is now kept while a session is transiently
absent, so only a new failing commit resurfaces it.
Both models now trace their decisions (`[BlockedSessions]`,
`[BlockedSessionsIndicator]`, `[SessionsTitleBar]`), covering the raw
blocked set per recompute, every acknowledgement added/kept/released with
its reason, blink queuing, the surfaced set, and command center widget
churn, so a resurfacing session can be diagnosed from the logs alone.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* sessions: correct SessionActionFeedback ownership documentation
Its class JSDoc still said the title bar widget owns the instance, which
contradicted the move of that ownership to SessionsTitleBarContribution.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Refactor AgentService instantiation
Create an agent-host application DI scope, construct AgentService through it, and remove child-to-parent service re-exports. Update tests to use the production construction path. (Written by Copilot)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Clarify Agent Host DI scope names
Use explicit bootstrap and application names for service collections and instantiation services. (Written by Copilot)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Share Agent Host service initialization
Use one strict DI scope and centralize common base and provider service setup for both Agent Host entry points. (Written by Copilot)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Streamline Agent Host runtime creation
Expose one runtime factory that owns common file, session, DI, AgentService, diagnostics, and optional provider infrastructure initialization. (Written by Copilot)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Move AgentService composition to runtime
Construct and register the AgentService core and collaborator graph outside AgentService, use one guarded initialization step for genuine back-references, and replace the test-only clock injection with virtual timers. (Written by Copilot)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Use one complete Agent Host runtime graph
Remove optional provider-infrastructure setup, make BYOK policy explicit, and defer Claude SDK environment mutation until first use. (Written by Copilot)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix Agent Host integration fixtures
Keep mock-provider configuration distinct from host-owned worktree settings and use an existing workspace for permission containment tests.
(Written by Copilot)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Restore Claude SDK initialization behavior
Keep the AgentService DI refactor focused by leaving the existing Claude SDK environment setup unchanged.
(Written by Copilot)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Align chat pill chevron and show entry paths in dropdown hovers
The chevron glyph is drawn above the middle of its box, so it read as sitting too high next to the label; nudge it onto the label's optical centre. Resource pills tighten their file icon slot and leading padding.
Customization entries now carry a hover with the path relative to the session folder that holds them (prefixed with the folder name when the session spans several), falling back to the absolute path. Artifact entries show their URI or link.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* sessions: Keep pill hover details accessible
Separate visual hover content from plain-text descriptions and actionable labels so artifact locations remain visible without replacing pill action names. Cover dropdown forwarding and single-entry ARIA labels.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* inline chat: add experimental Agent Host backend
Adds an experiment-gated (`chat.inlineChat.agentHost.enabled`, default off)
Agent Host backend for editor inline chat, following the terminal chat
migration. When disabled, inline chat behaves exactly as before.
Because the Agent Host writes files directly to disk rather than streaming
edits, review UI is hydrated from before/after snapshots instead:
- `InlineChatSessionResolver` picks the Agent Host or the legacy local
session, falling back on any failure and treating cancellation as
cancellation rather than fallback.
- `IChatEditReviewSession` is extracted as a narrow supertype of
`IChatEditingSession` so a surface can supply reviewable entries without
implementing checkpoints, storage, streaming edits or multi-diff.
`editingSessionsObs` is typed to it, keeping editor-level review UI
(decorations, hunk keep/undo, accessibility) working.
- `InlineChatEditReviewSession` implements only that surface. It saves and
snapshots the target, locks it read-only for the turn, and reuses
`ChatEditingModifiedDocumentEntry` so diffing and hunk review come for
free. Turns are bracketed with `startExternalEdit`/`stopExternalEdit` so
disk-driven model reloads render in real time and stay cumulative across
follow-up turns.
- `IFilesConfigurationService.updateReadonly` accepts an `IMarkdownString`
so a programmatic lock can explain itself instead of offering the generic
"set writeable" affordance.
Notebooks and untitled documents deliberately stay on the legacy path.
Also fixes a pre-existing leak where every non-local session was written to
the chat history index regardless of location, so throwaway inline (and
terminal) sessions appeared in the session list.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* inline chat: track off-target agent edits in real time
The Agent Host emits an `externalEdit` progress part as each tool call
completes, so files the agent touches outside the inline-chat target can be
discovered during the turn rather than only at its end.
`InlineChatEditReviewSession` now watches the response for those parts and
creates a review entry as soon as one appears, seeding its baseline from the
part's `beforeContentUri`. That baseline is the only trustworthy "before" for
an off-target file: the agent writes to disk before announcing the edit, so
reading current content would silently yield an empty diff. Entries enter
external-edit mode so subsequent disk reloads keep their diffs live, matching
the target file. `endTurn` keeps its sweep as an idempotent safety net.
Deletes and renames are skipped — neither maps cleanly onto a single-URI
`IModifiedFileEntry`.
Fixes two attribution races that would drop agent edits from the diff:
- A newly created entry was published through `entries` before external-edit
mode was on, so an observer could see it and a disk reload could land in
that window and be rebased into the baseline as a user edit.
- An off-target entry carried over from an earlier turn only re-entered
external-edit mode once its part arrived, but the disk write precedes the
announcement. All tracked entries now enter external-edit mode at
`beginTurn`.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* inline chat: add experimental Agent Host backend
Adds an Agent Host backend for editor inline chat. The setting
`chat.inlineChat.agentHost.enabled` controls it and is off by default. When the setting is
off, inline chat operates as before.
The Agent Host writes files to disk. It does not stream edits into the editor. Inline chat
therefore builds its review UI from before/after snapshots.
- Adds `InlineChatSessionResolver`. It selects the Agent Host session or the legacy local
session. It falls back to the legacy session on failure. It does not fall back when the
user cancels.
- Extracts `IChatEditReviewSession` as a supertype of `IChatEditingSession`. A surface can
supply reviewable entries without checkpoints, storage, streaming edits, or multi-diff.
Editor review UI, such as decorations and keep/undo, continues to operate.
- Adds `InlineChatEditReviewSession`. It saves and snapshots the target file, makes the file
read-only for the turn, and reuses `ChatEditingModifiedDocumentEntry`. Diff decorations and
hunk review operate without new diff code.
- Shows diff decorations in real time. Each turn starts and stops external-edit mode, so disk
reloads count as agent edits. The diff stays cumulative across turns.
- Tracks the files that the agent edits outside the target file. The Agent Host announces each
edit when a tool call completes. The baseline content comes from that edit.
- Lets `IFilesConfigurationService.updateReadonly` accept an `IMarkdownString`. A programmatic
lock can then show its own reason.
- Shows the current agent operation in the inline input placeholder.
- Keeps notebooks and untitled documents on the legacy path.
Makes throwaway (ephemeral) sessions start and run more quickly:
- Disables MCP servers, subagents, and custom agents for these sessions.
- Skips the turn-start checkpoint. This work is on the critical path of each turn.
- Skips title generation and the rename instruction. The title is never shown.
- Adds `enabledForEphemeralSessions` to server tool definitions. A tool must opt in before an
ephemeral session receives it.
Also keeps throwaway sessions out of the session lists. The host no longer sends
`root/sessionAdded` for an ephemeral session. The chat history index no longer stores an
external session from a transient surface.
(Commit message generated by Copilot)
* inline chat: address review feedback and fixture failures
- Adds `getEditingSession` to the two component fixture mocks of `IChatEditingService`. The
chat widget now calls this method, so the fixtures failed to render.
- Cancels the turn when the pre-turn save is cancelled. The buffer stays dirty in that case,
so the end-of-turn revert discarded the unsaved work of the user.
- Records a created off-target file with `ChatEditKind.Created`. A rejection then deletes the
file instead of leaving empty content on disk.
- Cancels the request when turn preparation fails. Before this change the agent could write
files while the file was not read-only and no review baseline existed.
- Examines the session map again after the Agent Host resolves. Before this change two
controllers for one file could each create a session.
- Corrects the comment about custom agents for ephemeral sessions. The SDK can still find
agents in the plugin directories.
(Commit message generated by Copilot)
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* agentHost: fix Copilot telemetry provider
Use the session URI scheme for tool-call and tool-approval compatibility events so Agent Host rows consistently report the canonical provider.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* agentHost: handle peer chat telemetry providers
Derive compatibility-event providers from the owning session so peer chat resources do not report the ahp-chat scheme.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Move the scenario runner out of the MCP server
The validate-ui-scenario skill runs `runScenario`, which drives VS Code through
`test/automation` and writes an evidence bundle. None of that is MCP: the runner
loads no MCP module at runtime, and the SDK import it inherited was type-only, so
TypeScript already elided it. It only lived under `test/mcp` because that is where
the evidence pipeline was first written.
That matters now: deleting the MCP server would take the skill with it.
Move the six files that have nothing to do with MCP into a new `test/scenario`
package, and leave `test/mcp` as one of its consumers alongside the skill. The MCP
evidence tools move to `test/mcp/src/evidenceTools.ts`, where the server-specific
schemas belong. Deleting `test/mcp` now removes only MCP code.
Drop the step banner along with it. `showOverlay` appended a banner to the DOM of
the product under test, which can shift layout and influence focus, so the runner
always opted out via VSCODE_EVIDENCE_CLEAN_CAPTURE. With the runner as the only
caller that opinion is unanimous, so the overlay and its opt-out both go and the
capture is unconditionally faithful. Step titles are still rendered onto the
finished recording by renderEvidenceChapters.
The new package emits declarations, matching `test/automation`, so the MCP server
keeps real types rather than silently degrading to `any`.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: adb443eb-11e5-40a1-8608-7f593fa79485
* Add the lockfile for the new scenario package
Registering `test/scenario` in `build/npm/dirs.ts` makes the root install run npm
in that directory, and CI uses `npm ci`, which requires a lockfile. Every other
package registered there has one, so a clean CI install failed immediately with
ENOENT on `test/scenario/package-lock.json` before anything compiled.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: adb443eb-11e5-40a1-8608-7f593fa79485
* Notify on the extracted scenario package
`test/mcp/**` notifies @TylerLeonhardt, so moving the runner to `test/scenario`
silently dropped notifications for it. Point the new path at the owner of the
validate-ui-scenario skill.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: adb443eb-11e5-40a1-8608-7f593fa79485
* Do not re-declare @types/node in the scenario package
The root package already declares `@types/node` as a devDependency, so the
extracted package inherits it through normal ancestor resolution; declaring it
again added a dependency that the OSS license check cannot cover, because
`@types/node` ships no LICENSE file and is not in ClearlyDefined or
cglicenses.json.
Verified against the state CI produces: `npm ci` in `test/scenario` installs no
`@types/node`, and both packages still compile, so the types resolve from the
repository root as intended.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: adb443eb-11e5-40a1-8608-7f593fa79485
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: adb443eb-11e5-40a1-8608-7f593fa79485
* Let the setup banner reload an agent's configuration
A user who finishes setup outside the app — `claude login` in a terminal,
an exported key — leaves no signal the app can see, so the banner kept
asking them to sign in to something they had already signed in to. Give
them a way to say "look again", and rename the docs link to "learn more"
now that it is one of two links rather than the only one.
The re-look is the tail of a download promoted to its own gesture:
restart chat discovery, then refresh models. `AgentSdkSetupChannel` grows
a second request key rather than per-agent code, so agent #3 still needs
no edit here — one consumed nonce per key, cleared as it is claimed, so a
repeat press still lands.
The reload clause folds into each of the four `noAccount` sentences
rather than trailing them: it is unconditional, so the table stays at
four branches and no localized string is assembled from fragments.
* Rank the no-account copy as the buttons rank it, and harden its links
Read the sentence in the order the routes are weighted: GitHub sign-in leads,
as the primary button; the provider sign-in follows; reload and docs trail,
being the copy's only links rather than buttons. Reload and docs become their
own sentences — kept as trailing clauses they would have fallen under the "if
you already set up Claude elsewhere" conditional, which does not scope docs.
Addresses review feedback: build both `command:` hrefs through
`createCommandUri` instead of by hand (`encodeURIComponent` leaves `)` alone,
so an agent id containing one closed the markdown link destination early), and
escape the host-supplied display name and sign-in provider before interpolating
them into markdown this banner trusts for two commands.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* Rewrite the no-account copy, and point Claude at its integrations docs
The four sentences now put every sign-in route and the reload into one "or"
list, ranked as the buttons rank them, and give the docs their own trailing
sentence. Claude's docs URL moves to the third-party integrations page, which
is what "other ways to set up Claude" actually means: Console, Bedrock, Vertex,
Foundry, Teams and Enterprise. "Set up" is the verb, two words, as the rest of
the string already had it.
Both agents' URL constants still described the workbench as labelling a button.
It has been a link since docs stopped being an action.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* agentHost: stop a failed git probe from stalling Agent Merge
A session with Agent Merge enabled could sit idle forever without ever
binding to its pull request, so review comments and CI failures never
reached it.
`_computeSessionGitState` treats every probe as independent and populates
fields best-effort. When `git status` failed -- most often a timeout under
load, which writes nothing to stderr and so logged nothing at all -- it
still returned an object, just without a branch. `_setSessionGitState`
replaces persisted git state wholesale, so that object overwrote the good
branch with `{"baseBranchName":"main"}`.
Nothing then repaired it. `AgentMergeController._evaluate` bails on a
missing branch before it reaches the refresh that would recompute it, and
the lazy refresh on subscribe only fires when git state is entirely
absent, so a partial state masked it. For a session held resident by Agent
Merge alone -- no client watching, no edits landing -- neither of the
remaining refresh triggers fires either, leaving it to re-read the same
stale state on the 10 minute backstop indefinitely.
- Return `undefined` from `_computeSessionGitState` when the status probe
fails, so callers keep the state they already had.
- Refresh git state in `_evaluate` before giving up on the branch, which
also recovers sessions already holding a branch-less state.
- Treat a branch-less state as missing in the subscribe-time refresh.
- Log git failures that produce no stderr, so a timed-out probe is no
longer invisible.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* agentHost: distinguish a detached HEAD from a failed git probe
Addresses PR review feedback: keying the repair off a missing `branchName`
alone also matched a detached HEAD, which reports no branch by design.
Those sessions would have refreshed git state on every evaluation --
a periodic git call and log noise that could never produce a branch.
`parseGitStatusV2` already recognises `(detached)`; it now reports that as
`isDetachedHead` so the distinction survives into persisted session git
state, and a shared `needsSessionGitStateRefresh` predicate keeps the
Agent Merge and subscribe-time call sites in agreement about which states
are worth recomputing.
The controller additionally caps the repair at one attempt per runtime, so
any other checkout that cannot report a branch costs a single git call
rather than one per backstop, and logs a warning when a refresh still
yields no branch.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* agentHost: stop the agent editing host-created attachment snapshots (#331154)
When a user-message attachment is not a plain, already-existing file:// file
(pasted content, an unsaved editor, or a read-only git: diff), the Agent Host
snapshots the bytes to .../agentSessionData/<session>/attachments/... and handed
the copy to the model as an ordinary editable file. The model then edited the
throwaway copy instead of returning the transformed content / editing the real
file. This is the uncovered sibling of #319314 (fix#319452 only exempted
existing file:// attachments).
Keep each snapshot visible (path preserved so the model can read it) but signal
it read-only so the agent does not edit it:
- Tag every host-created snapshot with a _meta marker (new
agentSnapshotAttachmentMeta) at the snapshot-write point and when a snapshot
copy is re-attached from the attachments folder.
- Copilot: send the snapshot as {type:'file', path} with a plain display name and
deliver a read-only note via the additionalContext channel (rendered as a
<system_reminder>) on the main turn; steering does not fire the
user-prompt-submitted hook, so its note is appended to the steering prompt as a
<reminder> block (stripped from the bubble, forwarded to the model).
- Codex: annotate the existing @path mention with "(read-only snapshot - do not
edit)".
- Claude: annotate the existing <system-reminder> path line likewise.
- Write-deny backstop: when a provider raises an interactive confirmation for a
write under the session attachments dir, the host hard-denies it before
auto-approval.
Known limitation: the write-deny only fires on an interactive permission prompt,
so in autopilot/bypass modes the read-only signal is advisory only. A
mode-independent guarantee (marking snapshots read-only on disk) is tracked as a
follow-up.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* agentHost: address PR review feedback for snapshot read-only handling (#331154)
- agentService: match the session attachments dir by URI containment
(extUriBiasedIgnorePathCase.isEqualOrParent) instead of a string prefix, so a
sibling like .../attachments-backup is not mis-tagged and a case-differing
snapshot path is still recognised. Applied to both _isRewritableAttachment and
_isUntaggedSnapshotResource via a shared _isUnderAttachmentsRoot helper.
- copilot: keep a snapshotted selection on the selection path (preserving the
selected text and range) instead of collapsing it to a whole-file attachment;
the read-only signal already rides the additionalContext/<reminder> note.
Removed the now-redundant snapshot branch in _toSdkAttachment.
- agentSnapshotAttachmentMeta: correct the doc — Copilot sends the file path and
conveys read-only out-of-band, it does not omit the path.
- tests: add an AgentSideEffects pending-confirmation test that a write under the
session attachments dir is hard-denied even with global auto-approve, with no
confirmation-ready action dispatched; update the Copilot snapshot-selection test
to expect the selection path.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>