Skip to content

chat: optional per-chat agent so one session can mix agents - #534

Open
Sandeep Somavarapu (sandy081) wants to merge 7 commits into
mainfrom
sandy081/agents/ahp-protocol-optional-agent-provider
Open

Sandeep Somavarapu (sandy081) wants to merge 7 commits into
mainfrom
sandy081/agents/ahp-protocol-optional-agent-provider

Conversation

@sandy081

@sandy081 Sandeep Somavarapu (sandy081) commented Oct 20, 2026 •

Copy link
Copy Markdown
Member

Lets one session contain chats backed by different agents (harnesses). For example, a Copilot session can hold a Claude chat that reviews the Copilot chat's work. Everything is additive and gated by a new host capability; hosts that don't advertise it, and clients that don't use it, behave exactly as today.

What's added to AHP

Capability

  • InitializeResult.multipleChatProviders?: {}: present when chats in one session may be backed by different agents. Clients MUST NOT send CreateChatParams.provider without it.

Per-chat agent (provider)

  • CreateChatParams.provider?: the agent for a new chat. Absent means the session's provider. Hosts reject with InvalidParams an unknown agent or one without multipleChats.
  • Forks and side chats (source present) continue the source chat's history, so they always use the source chat's agent.
  • ChatState.provider?, ChatSummary.provider?, SessionChatSummary.provider?: the chat's agent. Absent means the session's provider; hosts set it when it differs.
  • It never changes: like resource, it is an identity field omitted from session/chatUpdated changes. The TS session reducer strips it.

Per-chat config

  • ChatConfigState is unchanged: values stays an overlay of chatMutable keys.
  • For a chat whose provider differs from the session's, the session's config doesn't apply: clients get that agent's schema with the existing resolveSessionConfig for the chat's provider, and values overlays that agent's resolved values.

moveChat

  • A moved chat keeps its agent; after the move the host reports provider whenever it differs from the destination session's provider.
  • Hosts MAY refuse, as an unsupported destination, a move they can't support.

Compatibility

  • Additive: every field is optional and the capability is an empty object. Ships in the unreleased PROTOCOL_VERSION 1.1.0.
  • Kotlin: the new provider parameter shifts positional constructor arguments and componentN destructuring in ChatState, ChatSummary and SessionChatSummary. Rust: direct struct literals of those types must set it. Both have changelog notes.

Implementation

  • Types: types/common/commands.ts, types/channels-chat/{commands,state}.ts, types/channels-session/{state,actions,reducer}.ts.
  • Spec and docs: a short "Chat providers" section in docs/specification/chat-channel.md, plus docs/guide/state-model.md.
  • Regenerated schema and clients (dotnet, go, kotlin, rust, swift).
  • Fixtures: round-trips 076 (create-chat provider), 077 (initialize capability), 078 (chat summary provider), 080 (session chat summary provider); reducers 309 (chatUpdated ignores provider), 311 (configChanged replace creates config when absent).
  • Change fragments: 20261019-chat-provider, 20261019-kotlin-positional-chat-fields, 20261020-rust-chat-struct-literals.

Validation

  • npm test: 570 passing; reducers.ts 100% statement and branch coverage. npm run generate leaves only intended changes.
  • go test ./... and cargo test --workspace pass locally; Kotlin, Swift and .NET are covered by CI.

Consumer

VS Code implements this behind the experimental setting chat.agentHost.multipleChatProviders.enabled (off by default), validated with unit, Agent Host e2e and live UI tests, including real Copilot + Claude chats in one session. The VS Code PR follows once this is synced.

Let one session contain chats backed by different agents. Additive and
capability-gated; absent values keep today's single-agent behavior.

- InitializeResult.multipleChatProviders: host-level presence capability
  (not advertised on connections negotiating < 1.1.0).
- CreateChatParams.provider: agent for a new chat. Absent means the source
  chat's agent for forks/side chats, otherwise the session's provider.
  Hosts reject it without the capability, for unknown agents or agents
  without multipleChats, and when it differs from a source chat's agent.
- ChatSummary/ChatState/SessionChatSummary.provider: the chat's agent;
  absent means the session's provider, set whenever it differs.
- moveChat: with the capability, moved chats keep their agent and report
  it when it differs; without it, hosts MAY reject cross-provider moves.
- Per-chat config: a chat on a different agent starts from that agent's
  defaults instead of inheriting session values.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
`ChatConfigState.schema?: SessionConfigSchema` lets a chat carry its own
config schema, mirroring the session. Additive; changes still flow through
`chat/configChanged`.

- Hosts MUST set `schema` (the chat agent's chat-applicable config schema)
  when a chat's provider differs from the session's, with `values` seeded
  from that agent's defaults; they MAY omit it otherwise, and clients then
  use `SessionState.config.schema`.
- Clients MUST render and validate a chat's settings against its schema
  whenever present.
- Kotlin reducer: keep `config.schema` on `chat/configChanged` (it rebuilt
  ChatConfigState from values only). Shared reducer fixture 308 covers it.
- Round-trip fixture 079; docs, state-model, and change fragment updated.
- read-stability test: expect the three `@stability` markers the previous
  commit added to generated command files.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Review follow-ups on the per-chat provider and config schema.

- Rust reducer: initialize `ChatConfigState.schema` (compile fix).
- `ChatConfigState.schema` is set if and only if the chat's agent differs
  from the session's. With it, `values` is the full effective config and
  omitted properties take their schema defaults (including after
  `replace`); without it, `values` stays an overlay on the session config.
- Hosts MUST reject `chat/configChanged` keys invalid for the chat's
  effective schema.
- moveChat: the chat keeps its provider. Hosts with
  `multipleChatProviders` set `ChatState.provider` on movable chats and
  normalize `config` for the destination session. Clients resubscribe to
  observe a changed schema.
- `provider` is an identity field in `session/chatUpdated`. The TS session
  reducer strips it; native clients already merge an explicit field list.
- Honest compatibility wording for clients unaware of `provider`.
- Reducer fixtures 309 (chatUpdated ignores provider) and 310 (replace
  preserves schema). Change fragments updated, plus a Kotlin note on
  positional constructor/componentN changes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add `provider: None` to the `ChatState` and `ChatSummary` initializers in
the ahp crate's reducer tests, which stopped compiling when those structs
gained the optional `provider` field.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Spec text only; no protocol shape changes.

- Pre-1.1.0 connections: only a host advertising
  `multipleChatProviders` on some connection creates mixed-agent chats;
  other connections see `provider`/`schema` as ignored unknown fields.
- moveChat normalization: hosts publish `chat/configChanged` with
  `replace: true` when values change, clients resubscribe when `schema`
  changes, hosts MAY refuse moves needing normalization, and `schema` is
  otherwise fixed. Define "valid" (key and value), state that
  non-chatMutable session values don't carry over, and cover `newSession`.
- `ChatState.provider` is required on movable chats only when the host
  accepts cross-provider moves for them.
- configChanged rejection uses `schema`'s chatMutable properties; with a
  schema, `replace` may only change or drop chatMutable keys.
- "receivers SHOULD ignore" wording made consistent.
- Rust change fragment for struct literals; fixtures 080 (session
  catalog provider) and 311 (replace with absent config).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Same wire types; much less normative text, and no requirements without an
implementation.

- ChatConfigState.schema is a plain overlay base: when present it replaces
  the session's schema for the chat and its defaults replace the session's
  values; `values` stays a chatMutable overlay. Hosts set it when the
  chat's agent differs. Removes the full-effective-config, seeding,
  replace-carry, and lifetime rules.
- moveChat: drops the normalization block. A moved chat keeps its
  provider, hosts MAY refuse moves they cannot support, and clients
  resubscribe when a move changes `config`.
- ChatState.provider, the capability doc, CreateChatParams.provider, the
  Chat providers section, and state-model trimmed to essentials, with the
  repetition across the four places removed.
- Config-schema change fragment and reducer fixture descriptions updated.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread types/common/commands.ts Outdated
Comment thread types/channels-chat/commands.ts Outdated
Comment thread types/channels-chat/commands.ts Outdated
Comment thread types/channels-chat/commands.ts Outdated
Comment thread types/channels-chat/state.ts Outdated
- Capability doc: just what it allows and that clients need it to send
  `CreateChatParams.provider`.
- CreateChatParams.provider: absent means the session's provider; the
  source-chat rule applies only to forks and side chats.
- moveChat: the host reports `provider` on a moved chat whenever it
  differs from the destination session's, including a chat that had none.
- Drop `ChatConfigState.schema`: a chat on a different agent overlays that
  agent's config, whose schema clients get from `resolveSessionConfig`
  with the chat's provider. Removes the schema fixtures, the change
  fragment, and the Kotlin/Rust reducer changes for it.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants