Skip to content
Draft
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ changes accumulate. Track in-flight protocol changes via PRs touching
`NOTIFICATION_INTRODUCED_IN` maps in
[`types/version/registry.ts`](types/version/registry.ts).

## [0.10.0] — Unreleased

Spec version: `0.10.0`

## [0.9.0] — 2026-08-28

Spec version: `0.9.0`
Expand Down
1 change: 1 addition & 0 deletions clients/dotnet/release-metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"client": "dotnet",
"packageVersion": "0.9.0",
"supportedProtocolVersions": [
"0.10.0",
"0.9.0",
"0.8.0",
"0.7.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,20 @@ public enum ActionType
AutomationRunPrimarySessionChanged,
[WireValue("automationRun/cancelRequested")]
AutomationRunCancelRequested,
[WireValue("session/canvasSet")]
SessionCanvasSet,
[WireValue("session/canvasRemoved")]
SessionCanvasRemoved,
[WireValue("canvas/availabilityChanged")]
CanvasAvailabilityChanged,
[WireValue("canvas/trustChanged")]
CanvasTrustChanged,
[WireValue("canvas/incarnationChanged")]
CanvasIncarnationChanged,
[WireValue("canvas/titleChanged")]
CanvasTitleChanged,
[WireValue("canvas/iconChanged")]
CanvasIconChanged,
}

// ─── Action Envelope ─────────────────────────────────────────────────
Expand Down Expand Up @@ -2487,6 +2501,130 @@ public sealed record AutomationRunCancelRequestedAction
public ActionType Type { get; init; }
}

/// <summary>A canvas was admitted (opened) or its catalog entry changed.
///
/// Upsert semantics keyed by {@link CanvasEntry.resource | `resource`}: the
/// server dispatches this with the full entry to record a newly opened
/// canvas, or to republish it after a trust/availability/incarnation change
/// so subscribers following only the session channel stay in sync with
/// {@link CanvasState}. Never client-dispatchable: admission is through
/// `openCanvas` or host publication of a correlated, already-open native
/// instance under that command's admission rules. Both paths MUST use the
/// same singular identity-to-resource binding; repeated native observations
/// MUST NOT create a second entry. A stale/out-of-order delivery
/// (`canvas.revision` not strictly greater than the currently-recorded entry's
/// revision) MUST be rejected (no-op) rather than overwrite a newer entry with
/// older data.</summary>
public sealed record SessionCanvasSetAction
{
public ActionType Type { get; init; }

/// <summary>The canvas entry to add or update, matched by `resource`.</summary>
public required CanvasEntry Canvas { get; init; }
}

/// <summary>A canvas was logically closed.
///
/// Remove semantics keyed by `resource`: an unknown URI is a no-op. This
/// represents durable membership removal, not a client hiding a local
/// tab/view — see `closeCanvas`.</summary>
public sealed record SessionCanvasRemovedAction
{
public ActionType Type { get; init; }

/// <summary>Entry in {@link SessionState.canvases} to remove, matching {@link CanvasEntry.resource}.</summary>
public required string Resource { get; init; }
}

/// <summary>Replaces the canvas's live resolution state.
///
/// Dispatched by the host on every availability transition, including
/// initial resolution after admission by `openCanvas` or a correlated native
/// open, provider restart, and endpoint failure/recovery. A client-local page
/// reload or transient presentation credential renewal alone does not require
/// this action or a revision change.</summary>
public sealed record CanvasAvailabilityChangedAction
{
public ActionType Type { get; init; }

/// <summary>New {@link CanvasState.availability}.</summary>
public required CanvasAvailabilityState Availability { get; init; }

/// <summary>The {@link CanvasState.revision} this action results in. The reducer
/// MUST reject (no-op) this action if `revision` is not strictly greater
/// than the canvas's current `revision` — this is how stale/out-of-order
/// deliveries are consistently rejected across every canvas action, not
/// just this one.</summary>
public long Revision { get; init; }
}

/// <summary>Replaces the canvas's trust decision.
///
/// Dispatched by the host whenever the execution-trust decision for this
/// canvas's declared actions changes (e.g. a pending decision resolves, or an
/// administrator revokes a previously trusted source).</summary>
public sealed record CanvasTrustChangedAction
{
public ActionType Type { get; init; }

/// <summary>New {@link CanvasState.trust}.</summary>
public required CanvasTrustState Trust { get; init; }

/// <summary>The {@link CanvasState.revision} this action results in; see {@link CanvasAvailabilityChangedAction.revision}.</summary>
public long Revision { get; init; }
}

/// <summary>Records that the canvas's live endpoint was replaced by a fresh one for
/// the same logical instance (e.g. the owning provider restarted).
///
/// Renewing transient presentation credentials for the same live endpoint is
/// not endpoint replacement and MUST NOT trigger this action.
///
/// The host MUST dispatch {@link CanvasAvailabilityChangedAction} to
/// transition through `notLoaded`/`loading` around this change. Receivers
/// MUST reject in-flight `invokeCanvasAction` replies and stale server-pushed
/// callbacks addressed to a superseded `incarnation` — because `incarnation`
/// is opaque (see {@link CanvasIdentity.incarnation}), that rejection is
/// driven by the accompanying `revision` bump here, not by comparing
/// `incarnation` values for order.</summary>
public sealed record CanvasIncarnationChangedAction
{
public ActionType Type { get; init; }

/// <summary>New {@link CanvasIdentity.incarnation}. MUST differ from the previous value and MUST NOT be reused for this logical identity.</summary>
public required string Incarnation { get; init; }

/// <summary>The {@link CanvasState.revision} this action results in; see {@link CanvasAvailabilityChangedAction.revision}.</summary>
public long Revision { get; init; }
}

/// <summary>Replaces the canvas's display title.</summary>
public sealed record CanvasTitleChangedAction
{
public ActionType Type { get; init; }

/// <summary>New {@link CanvasState.title}.</summary>
public required string Title { get; init; }

/// <summary>The {@link CanvasState.revision} this action results in; see {@link CanvasAvailabilityChangedAction.revision}.</summary>
public long Revision { get; init; }
}

/// <summary>Replaces or removes the canvas's display icon.
///
/// This is presentation metadata only. It does not replace the live endpoint,
/// change the canvas incarnation, or replay any canvas effect.</summary>
public sealed record CanvasIconChangedAction
{
public ActionType Type { get; init; }

/// <summary>New {@link CanvasState.icon}; `null` removes the current icon.</summary>
public required Icon? Icon { get; init; }

/// <summary>The {@link CanvasState.revision} this action results in; see {@link CanvasAvailabilityChangedAction.revision}.</summary>
public required long Revision { get; init; }
}

// ─── Partial Summaries (action-discovered) ───────────────────────────

/// <summary>Partial equivalent of ChatSummary — every field is optional for delta updates.</summary>
Expand Down Expand Up @@ -2666,6 +2804,13 @@ public StateActionConverter()
["automationRun/sessionRemoved"] = typeof(AutomationRunSessionRemovedAction),
["automationRun/primarySessionChanged"] = typeof(AutomationRunPrimarySessionChangedAction),
["automationRun/cancelRequested"] = typeof(AutomationRunCancelRequestedAction),
["session/canvasSet"] = typeof(SessionCanvasSetAction),
["session/canvasRemoved"] = typeof(SessionCanvasRemovedAction),
["canvas/availabilityChanged"] = typeof(CanvasAvailabilityChangedAction),
["canvas/trustChanged"] = typeof(CanvasTrustChangedAction),
["canvas/incarnationChanged"] = typeof(CanvasIncarnationChangedAction),
["canvas/titleChanged"] = typeof(CanvasTitleChangedAction),
["canvas/iconChanged"] = typeof(CanvasIconChangedAction),
},
allowUnknown: true)
{
Expand Down
Loading