Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 28 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,35 @@ changes accumulate. Track in-flight protocol changes via PRs touching
`NOTIFICATION_INTRODUCED_IN` maps in
[`types/version/registry.ts`](types/version/registry.ts).

## [1.0.0] — Unreleased
## [0.9.0] — 2026-08-28

Spec version: `1.0.0`
Spec version: `0.9.0`

### Added

- Automation catalogue and automation-run channels, including self-contained host-defined event triggers and automation-origin messages, for shared trigger-based agent session workflows.
- Turn errors are durable response parts, and resumable errors can reopen the same turn through `chat/turnResume`.
- `InitializeResult._meta` for hosts to advertise implementation-specific extension capabilities in initialize responses.

### Changed

- Annotations now carry an `origin` with a required session URI and optional chat URI and turn ID.
- Renamed the failed session lifecycle value from `creationFailed` to `failed`.
- `TerminalSessionClaim` now requires the chat URI that owns the terminal.
- Terminal state now uses an explicit `running`/`exited` lifecycle that preserves exits without an exit code.
- Generated clients now preserve unknown values for nonexhaustive protocol enums and their discriminated unions.
- `SessionToolClientExecutionRequest.toolCall` is narrowed to a running tool-call state.
- Automation catalogue snapshots now use `AutomationState.entries`, whose items are named `AutomationEntry`.

### Removed

- Removed `error` from `changeset/contentChanged`; changeset failures are represented by `changeset/statusChanged`.
- Removed session-level forking from `createSession`; use chat forking instead.
- `ContentNotFound` is no longer an exported AHP error code; `-32006` remains reserved and unassigned.

### Fixed

- Chat reducers now derive `modifiedAt` from turn action data instead of local wall clocks.

## [0.8.0] — 2026-08-17

Expand Down
7 changes: 4 additions & 3 deletions clients/dotnet/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -477,15 +477,15 @@ this release negotiates.
receive buffer dropped a frame; the receive loop now grows the buffer after
copying the previous frame rather than before.

## [0.9.0] — 2026-08-27
## [0.9.0] — 2026-08-28

Implements AHP 1.0.0.
Implements AHP 0.9.0.

### Added

- Automation catalogue and automation-run channels, including self-contained host-defined event triggers and automation-origin messages, for shared trigger-based agent session workflows.
- Turn errors are durable response parts, and resumable errors can reopen the same turn through `chat/turnResume`.
- First-party .NET client with generated AHP 1.0 wire types, reducers, JSON-RPC and multi-host clients, and an integrated WebSocket transport targeting `netstandard2.0` and `net8.0`. (#206, #214)
- First-party .NET client with generated AHP 0.9.0 wire types, reducers, JSON-RPC and multi-host clients, and an integrated WebSocket transport targeting `netstandard2.0` and `net8.0`. (#206, #214)
- `ClientConfig.TimeProvider` enables deterministic request timeout, keep-alive, reconnect scheduling, and host timestamps.
- `InitializeResult._meta` for hosts to advertise implementation-specific extension capabilities in initialize responses.

Expand All @@ -499,6 +499,7 @@ Implements AHP 1.0.0.
- Generated clients now preserve unknown values for nonexhaustive protocol enums and their discriminated unions.
- `SessionToolClientExecutionRequest.toolCall` is narrowed to a running tool-call state.
- The .NET client now ships as the signed `Microsoft.VisualStudioCode.AgentHostProtocol` and `Microsoft.VisualStudioCode.AgentHostProtocol.Abstractions` NuGet packages.
- Automation catalogue snapshots now use `AutomationState.entries`, whose items are named `AutomationEntry`.

### Removed

Expand Down
2 changes: 1 addition & 1 deletion clients/dotnet/release-metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"client": "dotnet",
"packageVersion": "0.9.0",
"supportedProtocolVersions": [
"1.0.0",
"0.9.0",
"0.8.0",
"0.7.0",
"0.6.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2367,10 +2367,10 @@ public sealed record AutomationCreateRequestedAction
{
public ActionType Type { get; init; }

/// <summary>Client-chosen `ahp-automation:` URI that becomes {@link AutomationState.resource}.</summary>
/// <summary>Client-chosen `ahp-automation:` URI that becomes {@link AutomationEntry.resource}.</summary>
public required string Resource { get; init; }

/// <summary>Complete initial {@link AutomationState.definition}.</summary>
/// <summary>Complete initial {@link AutomationEntry.definition}.</summary>
public required AutomationDefinition Definition { get; init; }
}

Expand All @@ -2391,27 +2391,27 @@ public sealed record AutomationUpdateRequestedAction
{
public ActionType Type { get; init; }

/// <summary>Target {@link AutomationState.resource}.</summary>
/// <summary>Target {@link AutomationEntry.resource}.</summary>
public required string Resource { get; init; }

/// <summary>Editable {@link AutomationDefinition} fields to replace.</summary>
public required AutomationDefinitionPatch Changes { get; init; }
}

/// <summary>Add or replace one full automation state in
/// {@link AutomationCatalogState.automations}.
/// {@link AutomationState.entries}.
///
/// Existing entries are matched by {@link AutomationState.resource} and
/// Existing entries are matched by {@link AutomationEntry.resource} and
/// replaced in place. A previously unseen resource is appended.</summary>
public sealed record AutomationSetAction
{
public ActionType Type { get; init; }

/// <summary>Full new or replacement automation state.</summary>
public required AutomationState Automation { get; init; }
public required AutomationEntry Automation { get; init; }
}

/// <summary>Remove one automation from {@link AutomationCatalogState.automations}.
/// <summary>Remove one automation from {@link AutomationState.entries}.
///
/// Clients may dispatch this action only while the target advertises
/// {@link AutomationOperation.Remove}. The host revalidates that operation
Expand All @@ -2423,7 +2423,7 @@ public sealed record AutomationRemovedAction
{
public ActionType Type { get; init; }

/// <summary>{@link AutomationState.resource} to remove.</summary>
/// <summary>{@link AutomationEntry.resource} to remove.</summary>
public required string Resource { get; init; }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ public sealed record InitializeResult
public TelemetryCapabilities? Telemetry { get; init; }

/// <summary>Host-owned automation support. Presence means clients may subscribe to
/// `ahp-automations://` for {@link AutomationCatalogState}; absence means the
/// `ahp-automations://` for {@link AutomationState}; absence means the
/// host does not expose an automation catalogue or automation commands.</summary>
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public AutomationCapabilities? Automations { get; init; }
Expand Down Expand Up @@ -278,7 +278,7 @@ public sealed record ClientCapabilities
/// restrictions.
///
/// Capabilities describe implementation support.
/// {@link AutomationState.operations} remains authoritative for which
/// {@link AutomationEntry.operations} remains authoritative for which
/// definition mutations are currently allowed on a particular automation.</summary>
public sealed record AutomationCapabilities
{
Expand All @@ -295,7 +295,7 @@ public sealed record AutomationCapabilities
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public AutomationRunCancellationCapability? RunCancellation { get; init; }

/// <summary>Maximum terminal entries retained in {@link AutomationState.runs}. Active
/// <summary>Maximum terminal entries retained in {@link AutomationEntry.runs}. Active
/// runs are not counted toward the limit. Absence means the retention limit is
/// implementation-defined.</summary>
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
Expand Down Expand Up @@ -1612,7 +1612,7 @@ public sealed record RunAutomationParams
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public Dictionary<string, JsonElement>? Meta { get; init; }

/// <summary>Target {@link AutomationState.resource}.</summary>
/// <summary>Target {@link AutomationEntry.resource}.</summary>
public required string Automation { get; init; }

/// <summary>Durable client-generated idempotency key. Retrying with the same key and
Expand Down Expand Up @@ -1645,10 +1645,10 @@ public sealed record FetchAutomationRunsParams
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public Dictionary<string, JsonElement>? Meta { get; init; }

/// <summary>Target {@link AutomationState.resource}.</summary>
/// <summary>Target {@link AutomationEntry.resource}.</summary>
public required string Automation { get; init; }

/// <summary>Cursor previously received as {@link AutomationState.runsNextCursor}.
/// <summary>Cursor previously received as {@link AutomationEntry.runsNextCursor}.
/// Omit to request the first page not already included by the snapshot.</summary>
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Cursor { get; init; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ namespace Microsoft.AgentHostProtocol;
[JsonSerializable(typeof(AuthRequiredReason))]
[JsonSerializable(typeof(AutomationCancelledRunLifecycle))]
[JsonSerializable(typeof(AutomationCapabilities))]
[JsonSerializable(typeof(AutomationCatalogState))]
[JsonSerializable(typeof(AutomationCompletedRunLifecycle))]
[JsonSerializable(typeof(AutomationCreateCapability))]
[JsonSerializable(typeof(AutomationCreateRequestedAction))]
[JsonSerializable(typeof(AutomationDefinition))]
[JsonSerializable(typeof(AutomationDefinitionPatch))]
[JsonSerializable(typeof(AutomationEntry))]
[JsonSerializable(typeof(AutomationEventTrigger))]
[JsonSerializable(typeof(AutomationFailedRunLifecycle))]
[JsonSerializable(typeof(AutomationManualRunOrigin))]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,7 @@ public enum ResourceChangeType

/// <summary>Operations the host currently permits for an automation.
///
/// The list on {@link AutomationState.operations} is authoritative and may
/// The list on {@link AutomationEntry.operations} is authoritative and may
/// change over time. Clients MUST NOT infer permission from capabilities alone:
/// capabilities describe what the host implementation can support, while
/// operations describe what is allowed for this particular automation now.</summary>
Expand Down Expand Up @@ -4964,7 +4964,7 @@ public sealed record AutomationSessionOrigin
{
public SessionOriginKind Kind { get; init; }

/// <summary>Owning {@link AutomationState.resource}.</summary>
/// <summary>Owning {@link AutomationEntry.resource}.</summary>
public required string Automation { get; init; }

/// <summary>Owning {@link AutomationRunState.resource}.</summary>
Expand Down Expand Up @@ -5155,7 +5155,7 @@ public sealed record AutomationSessionTemplate
/// A definition combines the initial automation message, the session template
/// used for each run, and zero or more automatic triggers. Run history,
/// timestamps, and currently allowed operations live on
/// {@link AutomationState} rather than in the definition.</summary>
/// {@link AutomationEntry} rather than in the definition.</summary>
public sealed record AutomationDefinition
{
/// <summary>Human-readable automation name.</summary>
Expand Down Expand Up @@ -5216,13 +5216,12 @@ public sealed record AutomationDefinitionPatch
public Dictionary<string, JsonElement>? Meta { get; init; }
}

/// <summary>Authoritative state of one automation in the
/// {@link AutomationCatalogState.automations} catalogue.
/// <summary>Authoritative state of one automation in {@link AutomationState.entries}.
///
/// The host owns trigger evaluation, run claims, run retention, and operation
/// availability. Clients render this state and submit actions or commands; they
/// never run a fallback scheduler for a host-owned definition.</summary>
public sealed class AutomationState
public sealed class AutomationEntry
{
/// <summary>Stable `ahp-automation:/&lt;id&gt;` resource identifier.</summary>
public required string Resource { get; set; }
Expand All @@ -5236,7 +5235,7 @@ public sealed class AutomationState

/// <summary>Newest-first retained run summaries. This is a bounded window; use
/// {@link FetchAutomationRunsParams | fetchAutomationRuns} when
/// {@link AutomationState.runsNextCursor} is present.</summary>
/// {@link AutomationEntry.runsNextCursor} is present.</summary>
public required List<AutomationRunSummary> Runs { get; set; }

/// <summary>Opaque cursor passed as {@link FetchAutomationRunsParams.cursor} for the next older run-history page.</summary>
Expand Down Expand Up @@ -5265,10 +5264,10 @@ public sealed class AutomationState
/// Subsequent {@link AutomationSetAction | `automation/set`} and
/// {@link AutomationRemovedAction | `automation/removed`} actions keep the
/// catalogue synchronized and participate in normal reconnect replay.</summary>
public sealed class AutomationCatalogState
public sealed class AutomationState
{
/// <summary>Full automation states keyed by {@link AutomationState.resource}.</summary>
public required List<AutomationState> Automations { get; set; }
/// <summary>Full automation entries keyed by {@link AutomationEntry.resource}.</summary>
public required List<AutomationEntry> Entries { get; set; }

/// <summary>Opaque host-defined catalogue metadata.</summary>
[JsonPropertyName("_meta")]
Expand Down Expand Up @@ -5434,7 +5433,7 @@ public sealed class AutomationRunState
/// <summary>URI of this automation-run channel.</summary>
public required string Resource { get; set; }

/// <summary>Owning `ahp-automation:` URI matching {@link AutomationState.resource}.</summary>
/// <summary>Owning `ahp-automation:` URI matching {@link AutomationEntry.resource}.</summary>
public required string Automation { get; set; }

/// <summary>Immutable provenance describing how this run was created.</summary>
Expand Down Expand Up @@ -6283,7 +6282,7 @@ public sealed class SnapshotState
public AnnotationsState? Annotations { get; set; }

/// <summary>Automation catalogue state variant, when populated.</summary>
public AutomationCatalogState? Automations { get; set; }
public AutomationState? Automations { get; set; }

/// <summary>Automation run state variant, when populated.</summary>
public AutomationRunState? AutomationRun { get; set; }
Expand All @@ -6303,9 +6302,9 @@ public override SnapshotState Read(ref Utf8JsonReader reader, Type typeToConvert
{
result.AutomationRun = root.Deserialize(AhpJsonTypeInfo.Get<AutomationRunState>(options));
}
else if (root.TryGetProperty("automations", out _))
else if (root.TryGetProperty("entries", out _))
{
result.Automations = root.Deserialize(AhpJsonTypeInfo.Get<AutomationCatalogState>(options));
result.Automations = root.Deserialize(AhpJsonTypeInfo.Get<AutomationState>(options));
}
else if (root.TryGetProperty("turns", out _))
{
Expand Down Expand Up @@ -6344,7 +6343,7 @@ public override SnapshotState Read(ref Utf8JsonReader reader, Type typeToConvert
public override void Write(Utf8JsonWriter writer, SnapshotState value, JsonSerializerOptions options)
{
if (value.AutomationRun is not null) { JsonSerializer.Serialize(writer, value.AutomationRun, AhpJsonTypeInfo.Get<AutomationRunState>(options)); return; }
if (value.Automations is not null) { JsonSerializer.Serialize(writer, value.Automations, AhpJsonTypeInfo.Get<AutomationCatalogState>(options)); return; }
if (value.Automations is not null) { JsonSerializer.Serialize(writer, value.Automations, AhpJsonTypeInfo.Get<AutomationState>(options)); return; }
if (value.Chat is not null) { JsonSerializer.Serialize(writer, value.Chat, AhpJsonTypeInfo.Get<ChatState>(options)); return; }
if (value.Session is not null) { JsonSerializer.Serialize(writer, value.Session, AhpJsonTypeInfo.Get<SessionState>(options)); return; }
if (value.Terminal is not null) { JsonSerializer.Serialize(writer, value.Terminal, AhpJsonTypeInfo.Get<TerminalState>(options)); return; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ public static class ProtocolVersion
/// The current protocol version (SemVer MAJOR.MINOR.PATCH) this
/// generated source speaks.
/// </summary>
public const string Current = "1.0.0";
public const string Current = "0.9.0";

private static readonly string[] s_supported =
{
"1.0.0",
"0.9.0",
"0.8.0",
"0.7.0",
"0.6.0",
Expand Down
12 changes: 6 additions & 6 deletions clients/dotnet/src/AgentHostProtocol/Reducers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2350,7 +2350,7 @@ public static ReduceOutcome ApplyToAnnotations(AnnotationsState state, StateActi

/// <summary>Applies an action to the automation catalogue in place.</summary>
public static ReduceOutcome ApplyToAutomation(
AutomationCatalogState state,
AutomationState state,
StateAction action)
{
Guard.ThrowIfNull(state, nameof(state));
Expand All @@ -2363,29 +2363,29 @@ public static ReduceOutcome ApplyToAutomation(
return ReduceOutcome.NoOp;
case AutomationSetAction set:
{
int index = state.Automations.FindIndex(
int index = state.Entries.FindIndex(
automation => automation.Resource == set.Automation.Resource);
if (index < 0)
{
state.Automations.Add(set.Automation);
state.Entries.Add(set.Automation);
}
else
{
state.Automations[index] = set.Automation;
state.Entries[index] = set.Automation;
}

return ReduceOutcome.Applied;
}
case AutomationRemovedAction removed:
{
int index = state.Automations.FindIndex(
int index = state.Entries.FindIndex(
automation => automation.Resource == removed.Resource);
if (index < 0)
{
return ReduceOutcome.NoOp;
}

state.Automations.RemoveAt(index);
state.Entries.RemoveAt(index);
return ReduceOutcome.Applied;
}
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public void ReducerMatchesFixture(string name, string path)
RunFixture<ChatState>(initial, expected, actions, Reducers.ApplyToChat);
break;
case "automation":
RunFixture<AutomationCatalogState>(
RunFixture<AutomationState>(
initial,
expected,
actions,
Expand Down
5 changes: 3 additions & 2 deletions clients/go/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ tag whose matching `## [X.Y.Z]` heading is missing from this file.

## [Unreleased]

## [0.9.0] — 2026-08-27
## [0.9.0] — 2026-08-28

Implements AHP 1.0.0.
Implements AHP 0.9.0.

### Added

Expand All @@ -32,6 +32,7 @@ Implements AHP 1.0.0.
- Terminal state now uses an explicit `running`/`exited` lifecycle that preserves exits without an exit code.
- Generated clients now preserve unknown values for nonexhaustive protocol enums and their discriminated unions.
- `SessionToolClientExecutionRequest.toolCall` is narrowed to a running tool-call state.
- Automation catalogue snapshots now use `AutomationState.entries`, whose items are named `AutomationEntry`.

### Removed

Expand Down
Loading