v0.4.0

microsoft/agent-host-protocolv0.4.0Jun 19, 2026by github-actions[bot]

Upgrading?

Release notes summary

This release implements AHP 0.4.0, introducing a dedicated chat channel (ahp-chat:), robust state reducers for changesets and resource watches, and improved type safety for message origins. It also enhances metadata handling across the protocol and fixes critical encoding bugs in AnyCodable.

Key Highlights

  • Introduction of the ahp-chat: channel with full chat lifecycle management (createChat, disposeChat).
  • Implementation of missing state reducers (changesetReducer, resourceWatchReducer, annotationsReducer) for state mirroring.
  • Type-safe MessageOrigin and MessageKind replacing untyped AnyCodable.
  • Enhanced metadata support via the _meta field on various actions and state roots.
  • Fixes for AnyCodable encoding corruption of numeric types and unsigned integers.

Breaking Changes

  • SessionStatus is now an OptionSet (UInt32) instead of an Int.
  • ChangesetOperationTarget range targets now use a nested TextRange struct instead of flat integers.
  • ChatState structure flattened; 'summary' removed from state, moved to ChatSummary catalog entry.
  • ChatSummary.modifiedAt and ChatState.modifiedAt changed from numeric timestamps to ISO 8601 strings.
  • SessionChatsChangedAction removed, replaced by SessionChatAddedAction, SessionChatRemovedAction, and SessionChatUpdatedAction.

New Features

  • MessageOrigin struct and MessageKind enum for message origins.
  • ConfigPropertySchema.additionalProperties for dynamic object schemas.
  • ChangesetContentChangedAction for snapshot operations.
  • ChangesetOperation.group and ChangesetOperationStatus.disabled.
  • _meta field on per-turn chat actions for provider context.
  • SnapshotState.resourceWatch for seeding resource watch descriptors.
  • AnnotationsUpdatedAction for partial annotation updates.
  • ChatSummary.workingDirectory and interactivity flags.
  • ErrorInfo.meta for provider-specific error metadata.
  • RootState.meta for implementation-defined host metadata.

Full Release Notes

## [0.4.0] — 2026-06-19

Implements AHP 0.4.0.

### Added

- `MessageOrigin` struct and `MessageKind` enum now type `Message.origin`
  (previously an untyped `AnyCodable`); `MessageKind` covers `user`, `agent`,
  `tool`, and `systemNotification`, adding faithful agent- and tool-initiated
  origins.
- `ConfigPropertySchema.additionalProperties` — optional field describing the
  schema for object-typed config properties beyond those in `properties`.
- `ChangesetContentChangedAction` for full-replacement changeset file
  snapshots with optional operations and error details.
- `ChangesetOperationStatus.disabled` — new case for changeset operations
  that are currently unavailable and cannot be invoked.
- `ChangesetOperation.group` — optional identifier for grouping related
  changeset operations together in the UI.
- `_meta` (`meta`) field on the per-turn chat actions (`chat/turnStarted`,
  `chat/delta`, `chat/responsePart`, `chat/reasoning`, `chat/usage`,
  `chat/turnComplete`, `chat/turnCancelled`, `chat/error`) — optional
  provider-specific metadata so hosts can carry portable per-event context,
  such as attributing an event to a specific agent (e.g. a sub-agent acting
  within the turn).

### Changed

- `ToolResultSubagentContent.resource` is now specified as the spawned worker
  **chat** URI (`ahp-chat:/<cid>`), not a session URI — a tool-spawned
  sub-agent is a chat. Its doc now describes the correspondence with the worker
  chat's `ChatOrigin.tool` record (matching `toolCallId`), which remains the
  canonical representation of the spawn relationship.
- **BREAKING:** `SessionStatus` is now an `OptionSet` with a `UInt32` rawValue
  (was `Int`), an unsigned 32-bit bitset that preserves combined and unknown
  forward-compat bits. Combine flags with set-union (`∪` / `union`) and test
  membership with `contains(_:)`.
- **BREAKING:** `ChangesetOperationTarget`'s range target now carries a nested
  `TextRange` (`{start: {line, character}, end: {line, character}}`) instead of
  a flat `{start, end}` integer pair.

### Added

- `SnapshotState.resourceWatch` case and matching
  `MultiHostStateMirror.resourceWatches` slot, so `applySnapshot(host:snapshot:)`
  can seed an `ahp-resource-watch:` channel's descriptor (root URI, recursive
  flag, optional includes/excludes) alongside the existing root / session /
  terminal / changeset / annotations slots. `reset(host:)` / `reset()` clear
  the new slot.

### Fixed

- `AnyCodable.encode` no longer corrupts `NSNumber`-backed `Int`/`Double` values to `Bool`/`Int`. `NSNumber` is now special-cased before the generic Swift type arms, using `CFBooleanGetTypeID()` to distinguish boolean from numeric `NSNumber` instances.
- `AnyCodable.encode(to:)` now preserves unsigned integers above `Int64.max` (encoding `NSNumber` values whose `objCType` is unsigned via `uint64Value` instead of the signed `int64Value` fallback), and the `AnyCodable.swift` template in `scripts/generate-swift.ts` reproduces the full encode/equality logic so regenerating the scaffold no longer reintroduces the bug.
- `MultiHostClient`/host runtime now advertises the generated `SUPPORTED_PROTOCOL_VERSIONS` on `initialize` instead of a stale hard-coded `"0.2.0"`.
- Session reducers now apply `_meta` (`meta`) updates from every
  tool-call-scoped action, not only `session/toolCallStart`.

### Added

- `annotationsReducer` implemented; annotations conformance fixtures (210–219) now pass.

- `AnnotationsUpdatedAction` (`annotations/updated`) — partially updates an
  existing annotation's `turnId` / `resource` / `range` / `resolved` without
  resending its entries. Handled by the annotations reducer (no-op on unknown
  id).

- `ahp-chat:` channel for per-chat conversation state; `SessionState.chats[]` catalog; `SessionState.defaultChat?` input-routing hint; `ChatOrigin` provenance union; `createChat` / `disposeChat` commands.
- `SessionChatAddedAction`, `SessionChatRemovedAction`, and `SessionChatUpdatedAction` handling for incremental chat catalog updates.
- `ChatSummary.workingDirectory` — optional per-chat working directory. Falls back to the session's `workingDirectory` when absent.
- `SessionDefaultChatChangedAction` (`session/defaultChatChanged`) — updates `SessionState.defaultChat` to steer new input to the designated chat; absent value clears the hint.
- `ErrorInfo.meta: [String: AnyCodable]?` — optional provider-specific metadata bag on error payloads (serialized as `_meta`), mirroring the existing `meta` field on `UsageInfo` and other protocol types. Clients MAY inspect well-known keys here for richer, localised error UI.
- `RootState` now exposes an optional `_meta` property bag (`meta: [String:
  AnyCodable]?`) for implementation-defined agent-host metadata, such as a
  well-known `hostBuild` key carrying the host's build version/commit/date.
- `changesetReducer` and `resourceWatchReducer` — the two state reducers
  that were missing from the Swift client are now implemented, mirroring the
  canonical TypeScript reducers (and the Kotlin/.NET clients). `changesetReducer`
  folds `changeset/*` actions into `ChangesetState`; `resourceWatchReducer`
  treats `resourceWatch/changed` as a documented event pass-through. The
  fixture-driven reducer test no longer silently skips the terminal, changeset,
  and resourceWatch fixture families — they now decode and assert, with the
  remaining gaps (unknown-discriminant response part; the not-yet-implemented
  annotations channel) pinned by an explicit drift tripwire.

### Changed

- `ChatState` is now flat — the previous embedded `summary` has been replaced with inlined `resource` / `title` / `status` / `activity` / `modifiedAt` / `model` / `agent` / `origin` / `workingDirectory` properties. `ChatSummary` remains as the standalone catalog entry on `SessionState.chats`.
- `ChatSummary.modifiedAt` and `ChatState.modifiedAt` are now ISO 8601 `String` values instead of `Int64`/`UInt64` milliseconds.

### Added

- `ChatSummary.interactivity` / `ChatState.interactivity` (`"full" | "read-only" | "hidden"`) indicating how the user can interact with a chat. Absent defaults to `"full"`.

### Removed

- `SessionChatsChangedAction` (replaced by the three discrete chat-catalog actions above).

### Fixed

- Encode-fidelity: an unknown `StateAction` variant no longer re-encodes to
  `{}` (dropping its `type` discriminant and extra fields); the raw payload is
  preserved on decode and re-emitted verbatim.
- Forward-compatibility: unknown discriminants on wire-decoded discriminated
  unions (`ResponsePart`, `ToolCallState`, `TerminalClaim`,
  `TerminalContentPart`, `Customization`, and other evolvable unions) now decode
  to a raw passthrough and re-encode verbatim instead of throwing
  `DecodingError`, so a snapshot carrying an unknown variant still decodes and
  subsequent actions fold correctly.
- `ChangesetOperationResourceTarget` / `…RangeTarget` now encode their `kind`
  discriminant (previously a computed property excluded from `CodingKeys`, so it
  was dropped on encode).

Related Repositories