* agents window: apply session `_meta` once per update and keep it transactional
Follow-up to "agents window: keep workspace-less quick chats out of bogus
workspace groups". That change made `_meta` arrive on every `listSessions`
refresh, which turns two pre-existing quirks in `AgentHostSessionAdapter.update`
into per-session, per-turn-completion work:
- `_meta` was applied twice: once inline and again through the trailing
`setMeta`. With the promotion logic now living in `setMeta`, that also ran
`_promoteToQuickChatIfWorkspaceless` and `_computeWorkspace` twice. Apply it
once, up front, where the promotion still precedes the workspace rebuild.
- `setMeta` opened its own `transaction()`. `transaction()` never joins an
enclosing one - it constructs a `TransactionImpl` and `finish()`es it - so
moving the single application to the top of `update` would notify observers
of `_meta` / `isQuickChat` / `workspace` before `isArchived`, `isRead`,
`changes` and `activity` were applied, handing them a torn snapshot. Take an
optional `ITransaction` and use `subtransaction`, and thread the caller's
transaction from `update` and `_handleSessionSummaryChanged`. The two
standalone callers pass nothing and keep their own transaction.
Also pins the absent-`_meta` case on the wire (the item is built field by field
and `satisfies SessionSummary` cannot catch a dropped optional in either
direction), and corrects the session-database key named in the docs: the marker
is persisted as `agentHost.workspaceless` (`AH_META_WORKSPACELESS_DB_KEY`,
written by `AgentService`), never `copilot.workspaceless`.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8f4788b1-3307-4b88-b3fc-23565548de06
* agents window: drop the redundant workspace rebuild and finish the transaction threading
Addresses review feedback on this PR.
- `update()` still recomputed the workspace unconditionally after `setMeta`
had already rebuilt it from the same project / working directories and the
same `_meta`, so a `_meta`-bearing refresh paid for `_computeWorkspace()`
twice - exactly the duplicate per-turn work this PR set out to remove. The
fallback rebuild now only runs when the snapshot carries no `_meta`.
- `setChangesSummary` and `setActivity` wrote their observables with no
transaction, and `observableValue.set(v, undefined)` builds and finishes one
of its own, so they notified mid-way through the enclosing update. Both now
take an optional `ITransaction`, and `update()` /
`_handleSessionSummaryChanged` thread theirs through. Without this, threading
the transaction into `setMeta` alone left `SessionSummaryChanged` still torn:
`changes` is applied before `_meta`, so an observer of both ran once on the
new chip with the stale workspace, then again at the outer finish.
The new test covers that notification path and fails with the intermediate
`{ branch: undefined, files: 2 }` snapshot when the transaction is dropped.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8f4788b1-3307-4b88-b3fc-23565548de06
---------
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8f4788b1-3307-4b88-b3fc-23565548de06
* Update protocol version and refine working directory handling in chat and session states
* Refactor agent session handling to support multiple working directories
- Updated interfaces and implementations across various modules to replace single working directory references with arrays of working directories.
- Modified session creation and metadata handling to accommodate multiple working directories.
- Adjusted tests to reflect changes in session structure and ensure compatibility with the new working directory model.
- Enhanced workspace building functions to properly utilize multiple directories for session management.
* fix: correct indentation in turn function for better readability
* Update workingDirectory to workingDirectories in ProtocolServerHandler tests
* Enhance working directory handling across agent services
- Updated AgentService to truncate working directories for providers that do not support multipleWorkingDirectories.
- Modified CopilotAgent to handle multiple working directories and persist them correctly.
- Adjusted session launcher and session handler to support additional working directories.
- Added tests to verify working directory truncation and persistence behavior.
- Implemented computeWorkingDirectories function to derive the ordered set of working directories based on provider capabilities.
- Updated related tests to ensure correct behavior for multi-root and single-root workspaces.
agents window: keep workspace-less quick chats out of bogus workspace groups
Automation-created quick chats (and any quick chat seen after a reload or a
list refresh) rendered in the sessions sidebar under a section header labelled
with a raw session UUID instead of under "Chats".
Whether a session is a quick chat cannot be derived from its working directory
-- the host assigns workspace-less sessions a throwaway scratch cwd at
~/.copilot/chats/<sessionId> -- so it travels as an `agentHost.workspaceless`
marker on the generic `_meta` bag. `AgentService.listSessions()` overlays it
correctly, but the AHP `root/listSessions` round-trip dropped `_meta` on both
ends, so any session first materialized from a listing was classified as a
workspace session rooted at that scratch dir. `notify/sessionAdded` does carry
`_meta`, which is why a fresh quick chat looked right until the next refresh.
- carry `_meta` on the `listSessions` wire item (server) and map it back
(client -- the only IAgentConnection implementation, so this covers the
local host too)
- infer workspace-less in `_buildInitialSummary` from `config?.` so a
`createSession()` with no config matches what the agents themselves infer
- make the adapter session-kind monotonically promotable instead of frozen at
construction, so a mis-classified session heals when an authoritative
`_meta` arrives, and report the promotion even when the workspace was
already undefined so the list regroups
- persist the healed kind by overlaying the adapter live quick-chat state in
`_persistCache`, so a stale snapshot cannot resurrect the mis-classification
The provider tests could never have caught the wire drop -- the mock host
returns its stored metadata verbatim, bypassing both mappers -- so the wire
regression tests live at the protocol layer, and the provider tests cover the
promotion and the cache round-trip.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c93359f3-9a7d-411d-9adb-21bdda78c6af
* agentHost: Attribute telemetry to initiating window
Identify editor and Agents window AHP clients and propagate the initiating client type onto existing Agent Host and Copilot CTS telemetry. Preserve attribution for queued turns and reconnect with a fresh initialize when the host no longer remembers the client.\n\n(Written by Copilot)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* agentHost: Address telemetry attribution feedback
Share initialize-result application, index Copilot SDK sessions for constant-time telemetry attribution, and clarify the protocol server test parameter.\n\n(Written by Copilot)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* agentHost: Clarify Copilot session maps
Document the root AHP session ownership map separately from the flat SDK session telemetry index.\n\n(Written by Copilot)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* tweak names
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Propagate /btw transcript selections through sessions and agent host.
Hide the snapshot in the first side-chat prompt while preserving it in origin metadata.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Require explicit fork/sideChat source kinds throughout VS Code.
Remove the legacy ChatSource helper/fallbacks and update focused tests plus the sessions skill note.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reject tagged-but-unknown createChat sources instead of treating them as side chats or legacy forks.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
/btw now branches the active chat context into standard Agents tabs instead of a separate side chat surface.
This keeps hidden tool subagents out of the visible tab model while preserving compatibility with the legacy fork-based side chat protocol.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Route local renderer traffic through the Agent Host Protocol over MessagePort, expose a token-protected local socket endpoint, and retain a narrow IPC management surface.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Adopt AHP 0.6.0 workingDirectories property in agent host
Sync the generated agent host protocol copy to spec version 0.6.0
(agent-host-protocol @ 11f1a65e), which renames the singular
`workingDirectory` on session/chat state to a `workingDirectories`
array in preparation for multiroot session support.
This change is a pure, behaviour-preserving property adoption across all
consumers: sessions continue to use a single working directory. Reads of
a single directory now use `workingDirectories?.[0]`, field-copy sites
pass the array through unchanged, and writes from a single URI produce a
one-element array. No multiroot client support is added here (no
capability advertising, state actions, multi-folder workspaces, or UI);
those land in a follow-up milestone.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address PR review feedback
- Reject the not-yet-supported multiroot working-directory client actions
(session|chat/workingDirectorySet|Removed) in the handwritten dispatch path
so a client cannot mutate the synchronized working-directory set without the
agent actually reconfiguring its directory access. The protocol declarations
remain; only the operational dispatch is deferred until multiroot lands.
- Compare workingDirectories by array identity (not just the primary entry) in
the state manager summary-equality check, matching the immutable reducers and
SessionSummaryNotifier so secondary-directory changes still dirty the summary.
- Update ISessionWithDefaultChat / mergeSessionWithDefaultChat API docs to
describe a chat's workingDirectories subset overriding or inheriting the
session's full set, fixing links to the removed singular members.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix agentHost session-filter test for workingDirectories migration
The `sessionAdded notification filters out sessions outside the workspace`
test constructed session summaries with the removed singular `workingDirectory`
field, so the production filter (which now reads `workingDirectories[0]`) saw no
directory and dropped the in-workspace session. Update the two directly-built
summaries to the `workingDirectories` array form.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Migrate test createSession wire calls to workingDirectories
The createSession dispatch handler now reads the working directory from the
renamed `CreateSessionParams.workingDirectories` array (AHP 0.6.0), but several
node integration / e2e test call sites still sent the removed singular
`workingDirectory` field. Since the field is silently ignored, sessions were
created without a working directory and fell back to the default chats folder,
failing the Agent Host E2E workspace/fileOperations/hostFeatures suites across
all providers (wrong cwd cascades into file completions, renames, worktree
resolution, and cd-prefix stripping).
Send `workingDirectories: [dir]` from the shared `createProviderSession` helper
and the remaining direct wire call sites so the requested directory reaches the
session state again. The real VS Code client already sent the array form.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Adopt AHP 0.7.0 primaryWorkingDirectory + requiresPrimary
Re-sync the generated protocol copy to the multiroot spec at 148c716b (spec
version 0.7.0) and adopt the two follow-up changes:
- Capability flag renamed `MultipleWorkingDirectoriesCapability.immutablePrimary`
-> `requiresPrimary`, with the new "agent needs one directory designated as
its primary root" semantics. No consumers referenced the old name.
- New optional `primaryWorkingDirectory` field at both the session level
(CreateSessionParams / SessionMetadata -> SessionState + SessionSummary) and
the chat level (CreateChatParams / ChatState + ChatSummary). Mirror it through
the state<->summary projection layer exactly like `workingDirectories`:
createSessionState / createChatState / chatSummaryFromState /
mergeSessionWithDefaultChat, plus the state manager's summary projection,
field-equality check, SessionSummaryNotifier diff, and markSessionPersisted
propagation. The generated SessionChatUpdated partial-summary merge is
field-agnostic, so it carries the new field automatically.
Purely additive optional fields; no client multiroot behavior is added
(consumers still read `workingDirectories[0]` as the single effective root).
Also preserve the VS Code-local `CompletionItem.label` field (added in #326807,
ahead of the spec) which the verbatim re-sync would otherwise drop.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Reject unsupported working-directory actions via reconciliation path
Address PR review feedback on the multiroot working-directory action gate:
- Instead of silently dropping the four not-yet-supported
session|chat/workingDirectorySet|Removed client actions (which never echoed
the origin, leaving the client's optimistic write-ahead action pending until
reconnect), emit a rejection envelope through the normal reconciliation path.
Added AgentHostStateManager.rejectClientAction, which emits an ActionEnvelope
carrying the original ActionOrigin and a rejectionReason without running the
reducer (no synchronized state change), so the originating client rolls back
its optimistic action.
- Guard the write-ahead client reconcile (SessionStateSubscription /
ChatStateSubscription) so a rejected envelope is never applied to confirmed
state in any branch — this also prevents a broadcast rejection from leaking
the rejected action into a non-origin client's state.
- Add a table-driven test covering all four action types asserting no dispatch
and exactly one rejection envelope preserving the original origin.
- Simplify the create-session working-directory read to
`URI.parse(params.workingDirectories[0])` now the branch proves index 0 exists.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Refine primaryWorkingDirectory to per-chat read-only (AHP ea279d99)
Re-sync the protocol copy to spec ea279d99 (0.7.0) and adopt the refined
primaryWorkingDirectory design:
- Session has NO primary: `primaryWorkingDirectory` is removed from
SessionState / SessionSummary. The session is just the equal-peer
`workingDirectories` set. Dropped all session-level primary handling
(createSessionState, the SessionSummaryNotifier diff, _toSummary,
_summaryFieldsEqual, and markSessionPersisted propagation).
- Primary is per-chat, read-only, fixed at chat creation: kept the ChatState
<-> ChatSummary mirroring (createChatState / chatSummaryFromState) and carry
the chat's own primary through the session+default-chat composite
(ISessionWithDefaultChat gains its own primaryWorkingDirectory; the merge no
longer falls back to a session primary). It is not sent via `session/chatUpdated`
(the state manager only ever puts status/activity/title in those changes), so
it never mutates post-creation.
- Inputs `CreateSessionParams.primaryWorkingDirectory` (seeds the default chat's
primary) and `CreateChatParams.primaryWorkingDirectory` are synced; capability
`requiresPrimary` unchanged.
Also re-preserve the VS Code-local `CompletionItem.label` field (#326807, ahead
of the spec) that the verbatim re-sync drops.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Only arm protocol disconnect timeouts for clients that this server has observed disconnecting. Unknown client IDs may belong to local IPC and must remain pending.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* chat: add timestamps and elapsed time
* put behind setting!
* fix tests
* ah timestamp fixes and better animations!
* switch to use new protocol changes
* bump ahp version
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Fix client-tool completion routing to resolve providers by parent session while preserving the originating chat channel for provider callbacks.
Update agent host unit and protocol fixtures to dispatch chat actions on explicit AHP chat channels and keep subagent chat state separated from parent sessions.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Address PR feedback by requiring chat actions and tool confirmations to flow on their AHP chat channels. Fix disconnected-client cleanup to iterate chats explicitly, normalize telemetry ids, and unblock subscription hydration on errors.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
VS Code ships the Claude and Codex agents but not their native SDK
binaries; those are downloaded on demand from the CDN the first time a
session of that provider materializes, then cached on disk. The tarballs
are 70-95 MB, so on a cold cache the first session start blocked for
several seconds with no user feedback. Surface a progress indicator
instead.
Wire format: a generic, operation-agnostic `root/progress` notification
(mirrors the AHP spec), correlated to its originating request by a
`progressToken` the client supplies on `createSession` rather than naming
a domain object. Completion is signalled by `progress === total`; the
host emits a terminal frame with `total === progress`.
Downloader: `_fetch` accumulates received bytes and reads `Content-Length`,
firing a throttled host-level `onDidDownloadProgress` event keyed by
package id. Adds `isSdkResolvableWithoutDownload` / `canLoadWithoutDownload`
so eager/background callers (e.g. `listSessions` at startup) never kick off
a cold download.
Server (agent host): `createSession` records `{provider -> {session ->
token}}`; when a session materializes on first message and triggers the
cold download, the host-level event is fanned out as one `root/progress`
notification per waiting token (package id === provider id), building the
localized `message` ("Downloading {0} agent…"). Tokens are cleared on
materialize / dispose. Codex prewarm is gated so it can't trigger a cold
download.
Client (renderer): the new-session composer supplies a `progressToken` on
its eager `createSession`; `_handleProgress` correlates incoming frames by
token, shows a determinate (or byte-count) notification, and dismisses it
once `progress >= total`.
Vendors the protocol copy at AHP d35ed4a.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Flatten SessionState (metadata inline; no state.summary); SessionSummary
is the root catalog with ISO-8601 createdAt/modifiedAt.
- Move model/agent off SessionState/ChatState onto Message; drop
session/modelChanged, session/agentChanged, session/activeClientToolsChanged
actions; default model now derives from the last turn / draft selection.
- Host state manager: ISessionEntry storage, derived getSessionSummary,
unified _meta, and a SessionSummaryNotifier for summary-notification
bookkeeping.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adopt the agent host protocol update that moves from a single active client
per session to multiple active clients, and make the agent implementations
model and merge them correctly.
Protocol + UI/server:
- SessionState.activeClient? -> activeClients[]; session/activeClientChanged
is split into session/activeClientSet (upsert by clientId) and
session/activeClientRemoved; session/activeClientToolsChanged carries a
clientId.
- The UI adds itself via session/activeClientSet and never removes itself.
- The server removes a client from activeClients (and cancels its in-flight
client tool calls) on unsubscribe, and on reconnect when the client does
not resubscribe to a session where it was still active. Disconnect keeps
the client active during the grace window; the grace timeout removes it and
fails its pending tool calls if it never returns.
IAgent implementations:
- Replace setClientTools/setClientCustomizations with a per-client handle API:
getOrCreateActiveClient(session, {clientId, displayName}) -> IActiveClient
(mutable readonly-array tools/customizations accessors) and
removeActiveClient(session, clientId).
- Add shared node infra ActiveClientToolSet (per-session, clientId-keyed tool
registry with merge-by-name + ownerOf) and adopt it in Copilot, Claude and
Codex so multiple active clients' tools/customizations are stored, merged
(deduped, first-inserted client wins) for the SDK, and tool calls are
stamped to the owning client.
- Guard teardown races: Copilot invalidates an in-flight customization sync on
removeClient; Claude removes tools synchronously and serializes customization
removal through the session sequencer, and prunes cached handles on session
disposal.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Track multiple live transports per clientId so closing a newer overlapping transport falls back to an older live one instead of treating the logical client as disconnected. This prevents pending client tool calls from being force-failed while the owning client is still connected.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
A client tool call could be force-failed as "disconnected" while the
owning client was in fact still connected and actively sending frames,
destroying the tool call (and its pending confirmation) before the user
ever saw the confirmation prompt.
The disconnect-grace machinery decided a client was gone from two
signals: `IClientRecord.connection === undefined` (arm condition) and
`lastSeenAt` (grace-window delay). Two gaps made it misfire:
- `lastSeenAt` was only updated at handshake/disconnect, never on
ordinary inbound frames, so for a long-lived chatty client it was
stale by minutes. The arm delay `max(0, TIMEOUT - elapsed)` then
collapsed to 0 and the timeout fired immediately.
- `connection` is a single pointer per clientId. When a clientId is
reused across overlapping transports, closing the most-recent
transport clears `connection` even though another transport for the
same client is still live, so `connection === undefined` does not
imply the client is gone.
Fix: track liveness from real traffic by bumping `lastSeenAt` on every
inbound frame, and re-verify liveness when the timeout fires
(recency-based, not connection-based) before force-failing. If the
client is still alive, re-arm only while it still owns a pending tool
call so an active client never leaves a perpetual timer running.
Adds regression tests: a live client on a second transport survives
despite `connection === undefined`, and a silent owner still fails after
the grace window.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Implement multi-chat session support for Copilot CLI in Local Agent Host Provider
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address CCR feedback: session-rename telemetry, createChat race, observable read
- Add dedicated onDidRenameSession event + agents/sessionRenamed telemetry
so session-title renames are no longer misclassified as chat renames
- Re-check chat existence inside the per-session sequencer in createChat to
avoid a race overwriting/disposing an already-registered conversation
- Cache mainChat.title read in chatCompositeBar autorun
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* agentHost: dispatch default-chat turn lifecycle on session URI
After merging origin/main's default-chat compat layer, turn-lifecycle
actions (turnStarted, truncated, turnCancelled) must target the session
URI for the default chat (and the peer chat URI for peer chats), so the
server routes them to the default chat and subagent session URIs derive
correctly. Conversation side-channel actions and tool-call observation
keep using the resolved chat URI.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* chore: sync agent-host-protocol to b55919a (multi-chat)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* refactor: route turn access via defaultChat helpers
Adopt the multi-chat Agent Host Protocol: turns, active turn, steering,
queued messages and input requests moved off the session and onto a
per-chat channel. A single default-chat compatibility layer recombines
the session with its default chat so existing consumers keep working:
- Server (AgentHostStateManager) routes chat actions to the default chat
and exposes a merged ISessionWithDefaultChat view.
- Client (AgentHostSessionHandler) subscribes to both the session and its
default chat channel and merges them for reads.
- Widen action dispatch/emit signatures to SessionAction | ChatAction and
migrate turn/tool-call/input wire strings from session/* to chat/*.
- Update tests and mock connection to serve ChatState for default-chat
subscriptions and route chat actions accordingly.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test: adapt post-merge tests to multi-chat Chat* action names
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: repair chat retrieval and config regressions from multi-chat adoption
The multi-chat AHP adoption introduced two classes of regressions:
1. Chat retrieval/echo routing: chat actions now emit on the derived
default-chat channel, but several consumers were still keyed on the
session URI. Await both the session and default-chat subscriptions
before reading history, restore the chat channel on subscribe, forward
ChatActions, and normalize chat<->session URIs in dispatch/side-effects.
2. Session config loss: getSessionState() now returns a merged composite
copy (mergeSessionWithDefaultChat), and _ensureDefaultChat replaced the
live map entry with a clone. Callers mutating the returned object
(state.config = ...) stranded the mutation on a throwaway. Fix:
_ensureDefaultChat mutates in place to preserve createSession/
restoreSession return identity, and a new setSessionConfig() mutates the
live map object for the async restore path.
Tests migrated to setSessionConfig() where they previously seeded config by
mutating getSessionState() returns; duplicate-restore and client-echo tests
updated to reflect the two-channel (session + default-chat) model.
All agentHost node suites (409) and the browser contribution suite (148)
pass; typecheck and valid-layers-check are clean.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* chore: sync agent-host-protocol to d7f592e (chat interactivity)
Regenerates the vendored protocol/ from agent-host-protocol @ d7f592e,
which adds the optional `interactivity?: "full" | "read-only" | "hidden"`
metadata to `ChatState`/`ChatSummary` and the `ChatInteractivity` type.
Mechanical sync only (scripts/sync-agent-host-protocol.ts); no hand edits.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* refactor: preserve chat interactivity across summary/state helpers
Carries the new optional `interactivity` field through the VS Code glue
converters so it round-trips between `ChatState` and `ChatSummary`
(`createChatState`, `chatSummaryFromState`) and re-exports the
`ChatInteractivity` type alongside the other chat types.
Absence still defaults to "full", so single-default-chat sessions are
unchanged; this just stops the field being dropped during denormalization.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* agentHost: sync protocol to 83fae5a (ChatInteractivity const enum)
Re-sync the vendored agent-host-protocol from a685a3e to 83fae5a, which
refactors ChatInteractivity from a string-union type to a const enum.
Pure type-level change with identical runtime string values; glue code
only passes interactivity through, so no source changes required.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* address PR review feedback on multi-chat adoption
- sync protocol to b88a4a3: Snapshot.state union now includes ChatState,
dropping the IStateSnapshot widening shim (con1)
- handler: resolve the default chat URI from the live session state's
defaultChat field instead of assuming a URI structure, with the
deterministic fallback clearly marked as temporary migration (con2, con3)
- protocolServerHandler: reject createChat for non-default chats with a
ProtocolError instead of silently succeeding (ccr2)
- agentSubscription: make getPendingActions type-honest by widening the
pending-action type to SessionAction | ChatAction and renaming the
channel field, removing the unsafe cast (ccr3)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix turn telemetry test stranded config mutation
getSessionState returns a detached composite (session merged with its
default chat), so mutating its config stranded the change. Use the
stateManager.setSessionConfig API to write the authoritative session
state instead, matching what agentService does in production.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test: route protocol integration tests through default chat channel
The multi-chat protocol adoption moved conversation contents (turns,
activeTurn, queued/steering messages, input requests) onto the session's
default chat channel, and renamed turn/tool-call/responsePart actions from
`session/*` to `chat/*`. The Protocol WebSocket integration tests still
subscribed only to the session channel and dispatched/awaited the old
`session/*` action types, causing notification timeouts and reads of
undefined turn arrays.
- Add `fetchSessionWithChat` helper that subscribes to both the session and
its default chat channel and returns the merged ISessionWithDefaultChat view
- Update turnExecution, sessionFeatures, sessionLifecycle, multiClient to read
turn/conversation state via the merged helper
- Rename stale `session/*` conversation actions to `chat/*` in clientTools,
toolApproval, turnExecution, and realSdkTestHelpers
- Subscribe secondary multi-client clients to the default chat channel
- Fix copilotRealSdk usage assertion to read turns from the default chat
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test: assert chat/turnStarted in AgentHost smoke test
The multi-chat protocol adoption dispatches turns as `chat/turnStarted`
on the session's default chat channel instead of `session/turnStarted`.
Update the Agents Window smoke test to look for the new action type in
the AHP JSONL transcript.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* agentHost: re-pin protocol to b3319ca (multi-chat merged to main)
The multi-chat sessions change landed on agent-host-protocol main as
b3319ca ("feat: multi-chat sessions (#213)"). The vendored copy was
previously synced from the PR branch (b88a4a3); the generated types are
byte-identical, so this only advances the .ahp-version pin to the merged
commit.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- A window reload reconnects with a new clientId but an identical tool
list. The cached SDK session was reused with the dead window's clientId
baked in, so every later client tool call was stamped with that dead id,
routed to passive render, and hung forever with nobody to invoke it.
- Introduces a live, shared ActiveClientState so the owning clientId is
read at tool-call stamp time instead of being frozen at session creation,
and makes the staleness check restart only on structural tool/plugin
changes — a clientId-only change no longer needlessly restarts (Copilot)
or yield-rebinds (Claude) the session.
- Adds a server-side safety net so a client tool call stamped for a client
that is not connected (including one that disconnected before the call was
even issued) fails after a grace window rather than hanging, with the
grace measured from when that client was last seen.
- Lets a completion that races ahead of the SDK tool handler resolve via a
buffered result, preserving the previous out-of-order tolerance.
Fixes#319641
(Commit message generated by Copilot)
* Bring over the latest version of the protocol
* Phase 1 & Phase 2
* Phase 3
* Phase 4
* Phase 5
* Fix diff information in the sessions list
* Wire-up the resolving the changesets when the session is active
* More fixes following the branch
* Attach the changesets for a materialized session
* Manually fix agent host protocol file
* 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>
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Fix compilation errors
* Skip/fix some tests
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Syncs the agent-host-protocol types to upstream 740f6cf and adopts the breaking changes from PR 188, while reverting PR 191 (Changeset/ChangesSummary rename) which we are not ready to adopt yet.
- Replaces `toolClientId` on tool-call actions with `contributor: { kind, clientId }` so the renderer/server can distinguish client vs MCP-provided tools
- Plumbs the new `ChangesetOperationStatus` field through commit-operation provider, tests, and reducer fixtures
- Adds the required `enabled`/`state` fields to McpServerCustomization stubs across tests and pluginParsers
- Narrows `ISyncedCustomization.customization` and related helpers to `PluginCustomization` so per-plugin `children`/`load` access is type-safe
- Reverts PR 191 changes in the synced protocol files: keeps `ChangesetSummary` (with `additions`/`deletions`/`files`) and drops `ChangesSummary` from `SessionSummary`
(Commit message generated by Copilot)
- Fix resource-watch reducer to properly handle single-variant union type
- Update test to expect re-encoded URIs from watch changes (file:// -> vscode-agent-host://)
- Add import of toAgentHostUri in test file for URI transformation
All 10558 tests passing locally.
Extend the AHP protocol with full filesystem operation support:
- Add resourceResolve, resourceMkdir, and resourceCopy methods to make efficient, single-round-trip filesystem operations
- Implement createResourceWatch with lazy watcher materialization (FS watcher only created on first subscribe)
- Add 30-second grace period and replay buffer for watch channel unsubscribe to support reconnections
- Surface watch setup errors via onDidWatchError event for proper error propagation
- Make resourceCopy, resourceResolve, resourceMkdir required (not optional) in IRemoteFilesystemConnection contract
- Extend both client→server and server→client (reverse-RPC) channels with all new methods
- Add comprehensive unit tests: watch lifecycle, error handling, includes pattern forwarding, copy/resolve/mkdir operations
- All tests passing (37 total, 7 new), TypeScript compilation clean, layer validation clean
Wires up the new `channels-otlp/` protocol so the agent host's
`ILogService` is mirrored over an `ahp-otlp://logs/{level}` channel
and surfaced as a per-host Output channel in the workbench.
- Add `OtlpLogEmitter` / `OtlpEmitterLogger` (`platform/agentHost/common/otlp/`).
`LogService` is constructed with the OTLP logger as a secondary sink in both
`agentHostMain.ts` and `agentHostServerMain.ts` so every log call fans out
to subscribers.
- `ProtocolServerHandler` advertises `telemetry.logs` in
`InitializeResult`, routes `subscribe`/`unsubscribe` on `ahp-otlp:`
channels through a typed `ChannelSubscription` union, canonicalises the
channel URI per-level, and broadcasts `otlp/exportLogs` notifications
filtered per subscriber severity.
- `RemoteAgentHostProtocolClient` stores the full `InitializeResult`,
adds `subscribeStateless` and `onDidReceiveOtlpLogs`.
- New `RemoteAgentHostLogForwarder` in the workbench layer registers an
`Agent Host (${host})` Output channel via `IOutputChannelRegistry`,
subscribes at the workbench's `ILogService` level (re-subscribes on
change), and appends decoded records. Constructed from
`remoteAgentHost.contribution.ts::_setupConnection` so it covers
WebSocket, SSH and tunnel paths. Local agent host IPC logging is
unchanged.
- Existing remote IPC traffic channel renamed to `Agent Host IPC (${host})`
to disambiguate from the new OTLP-derived channel.
- Move `UriTemplate` from `workbench/contrib/mcp/common/` to
`base/common/` so the forwarder can use it for `{level}` expansion.
Tests: 8 unit tests for the emitter, 7 for the server-side OTLP routing
(including URI canonicalisation), 3 integration tests for the
end-to-end wire flow, and the existing protocol/handshake/reconnect
suites.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* agentHost: adopt channel-based AHP wire model
Adopts the channel-based Agent Host Protocol revision from
microsoft/agent-host-protocol#127. Subscriptions, action delivery, and
protocol notifications now all route over named channels identified by a
top-level `channel: URI` field, replacing the pre-channels subscribe /
dispatch / notify model.
- Renames `ROOT_STATE_URI` from `agenthost:/root` to `ahp-root://` and
adds an `isAhpRootChannel` helper that tolerates both the canonical
wire form and the workbench `URI` round-tripped form (`ahp-root:`).
- Threads a `channel` parameter through every action dispatch path:
`IAgentService.dispatchAction`, `IAgentConnection.dispatch`,
`AgentHostStateManager.dispatchServerAction` / `dispatchClientAction`,
and `AgentSubscriptionManager.dispatchOptimistic`. Individual action
payloads no longer carry `session` / `terminal` / `changeset` fields;
the envelope carries the channel.
- Replaces the `{ method: 'notification', params: { notification } }`
wrapper with top-level JSON-RPC methods (`root/sessionAdded`,
`root/sessionRemoved`, `root/sessionSummaryChanged`, `auth/required`).
Each notification's params now carry their own `channel: URI` field.
- Renames the per-command identifying field to `channel` for the channel-
scoped commands (`createSession`, `disposeSession`, `createTerminal`,
`disposeTerminal`, `fetchTurns`, `completions`,
`invokeChangesetOperation`). Connection-level commands hard-code the
root channel string.
- Updates the subscribe result to `{ snapshot? }` and the
`SubscribeParams` / `UnsubscribeParams` field name from `resource` to
`channel`.
- Drops the removed `ProtocolNotification`, `NotificationType`,
`NotificationMethodParams`, and `NotificationMap` exports; provides a
thin compat `NotificationType` constant keyed by method name for
consumers that still discriminate notification variants.
- Adapts `AgentSubscription` routing to switch on `envelope.channel`
rather than inspecting per-action URI fields (`_isRelevantEnvelope`
replaces `_isRelevantAction`).
- Migrates all producers (`agentService`, `agentSideEffects`,
`agentConfigurationService`, `agentHostChangesetService`,
`agentHostTerminalManager`, `agentHostStateManager`,
`protocolServerHandler`, Claude/Copilot agent sessions, terminal
manager, etc.) and consumers (workbench session handler, terminal
contribution, pty, customization harness, provider tests) to the new
shapes.
(Commit message generated by Copilot)
* agentHost: address Copilot review for channels migration
Addresses feedback from Copilot's review of #317251:
- Update real-SDK integration test helpers and protocol integration
tests to use the channel-based command shapes (rename `session` /
`resource` to `channel`, add `channel: 'ahp-root://'` to
connection-level commands, drop `session` from action payloads, route
via params-level `channel`).
- Treat the canonical `ahp-root://` and the URI-normalized `ahp-root:`
forms as equivalent in `_isRelevantToClient` so root broadcasts reach
a client that subscribed using either form (mirrors `isAhpRootChannel`
already in use elsewhere).
- Drop a `..` hop from the `NotificationType` import path in the local
and remote session-provider tests; import from
`state/sessionActions.js` directly.
- Drop a stale post-merge `session: SESSION_STR` in
`claudeMapSessionEvents.test.ts:Test 9.5` and the matching
`requestResourceAccess` test expectation in
`agentHostFileSystemProvider.test.ts` so they match the new
channel-based shapes (root cause of the macOS browser CI failure).
(Commit message generated by Copilot)
* tests
* import
* fix: update changeset labels and descriptions to reflect branch changes
* Update tests
* feat: implement per-turn changeset recompute logic and associated tests
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* updates
* Refactor comments in agentService tests to clarify handling of git state in transient sessions
* Improve teardown logic in sessionDiffs integration test to handle Windows file locks
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
- Introduced a new module for building and parsing changeset URIs, including session-wide, uncommitted, and per-turn changesets.
- Implemented functions to construct and validate changeset URIs, ensuring they adhere to expected formats.
- Added comprehensive unit tests to verify the correctness of URI builders and parsers, covering various edge cases and malformed inputs.
- Ensured that the new functionality integrates seamlessly with existing session management features.
Replaces the exact-match version check in `ProtocolServerHandler._handleInitialize`
with a semver-caret negotiator (`negotiateProtocolVersion`) that picks the
highest offered version compatible with the server.
When negotiation still fails and the agent host was spawned by a managing
VS Code CLI (signalled by the new `VSCODE_AGENT_HOST_MANAGEMENT_SOCKET`
env var), the `UnsupportedProtocolVersion` error advertises a
`_meta.vscodeUpgradeMethod = "_vscodeUpgrade"` hint. The client can then
invoke that method on the same transport (callable pre-`initialize`) to
request a server upgrade.
The CLI runs a hyper-based HTTP control server on a unix socket / named
pipe. `POST /upgrade` synchronously downloads the latest release, returns
a serde-derived response (`{ok, upgradeNeeded, upgradeStarted,
runningCommit, latestCommit, restartDelayMs, error}`), and then schedules
a kill+respawn after a 3 s drain delay so the response can hop back
through the proxy before the transport drops. Single-flight via
`upgrade_in_progress: AtomicBool`; the listener is started lazily from
`AgentHostManager::start_server`.
UI:
- `RemoteAgentHostConnectionStatus.incompatible` carries `vscodeUpgradeMethod`
(read from `_meta`).
- A `watchForIncompatibleNotifications` autorun on each provider raises a
one-shot warning notification on transition into `incompatible`, with
"Update Server" (when the host advertised it) and "Show Options"
primary actions.
- `runServerUpgrade` is shared between the notification action and the
per-host quickpick. It drives a progress notification with a per-second
"Restarting in Ns..." countdown and observes `connectionStatus` so it
bails out if some other code path is already reconnecting.