Skip to content

[wip] Add local-canvases protocol contract (LC-17) - #443

Draft
Ulugbek Abdullaev (ulugbekna) wants to merge 3 commits into
microsoft:mainfrom
ulugbekna:ulugbekna/local-canvas-protocol
Draft

Ulugbek Abdullaev (ulugbekna) wants to merge 3 commits into
microsoft:mainfrom
ulugbekna:ulugbekna/local-canvas-protocol

Conversation

@ulugbekna

@ulugbekna Ulugbek Abdullaev (ulugbekna) commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Introduces a versioned, renderer-neutral ahp-canvas: channel for local extension/package-rendered canvas surfaces, promoting the VS Code local-canvas PoC transport into the canonical AHP contract (LC-17). This is coordinated with an independent VS Code local-canvas integration; the VS Code side is expected to sync this contract via the repo's existing protocol-sync script once merged.

What's included

  • Stable identityCanvasIdentityKey (exact backing chat, extension/package source, canvas type, instance id) plus a host-assigned, opaque incarnation token that MUST NOT be reused for a superseded logical instance. CanvasPackageSource carries a dedicated sourceId distinct from display-only packageName/version, since the same package name can be installed in more than one scope.
  • Lifecycle stateCanvasTrustState (trusted/pending/blocked) and CanvasAvailabilityState (unsupported/notLoaded/loading/empty/ready/failed) are independent axes. SessionState.canvases is durable membership, distinct from the new read-only listCanvasTypes type-discovery catalogue.
  • Commands:
    • listCanvasTypes — read-only discovery of canvas types available for a chat; never opens/materializes anything.
    • openCanvas — requestId-scoped idempotency (same requestId + identical params dedupes; different params rejects Conflict; a genuinely new requestId for an already-open identity may re-invoke the provider without creating a second logical identity).
    • resolveCanvasSource — read-only current-state/live-endpoint read (CanvasSourcePresentation { url, expiresAt? }, renderer-neutral and never persisted); never opens/resumes/restarts anything. Also used for client-local page reload and unavailable-source retry.
    • invokeCanvasAction — required incarnation precondition (rejects Conflict if stale); no exactly-once-across-crash promise; result never persisted into durable state.
    • restartCanvasProvider — the only operation that intentionally bumps incarnation; required requestId + incarnation preconditions.
    • closeCanvas — required revision precondition; logical close (durable membership removal), distinct from a client merely hiding a tab/view.
  • Capability — new server/runtime-side CanvasCapabilities / InitializeResult.canvases, independent of the existing client-side ClientCapabilities.canvases and of protocol-version negotiation alone. A protocol version does not by itself imply a working canvas runtime.
  • Bounds — declared, enforceable size/depth limits (CANVAS_REQUEST_ID_MAX_LENGTH, CANVAS_IDENTITY_FIELD_MAX_LENGTH, CANVAS_SCHEMA_MAX_PROPERTIES/CANVAS_SCHEMA_MAX_DEPTH, CANVAS_MAX_DECLARED_ACTIONS, CANVAS_INPUT_MAX_LENGTH/CANVAS_RESULT_MAX_LENGTH) plus an isCanvasSchemaWithinLimits enforcement helper, to give hosts a precise contract for LC-28.
  • Consistent staleness rejectioncanvasReducer and sessionReducer's session/canvasSet branch both reject any action/update whose asserted revision is not strictly greater than the current revision, uniformly across all fields (not just incarnation).

Version / generation

  • Keeps PROTOCOL_VERSION at 0.9.0, following the existing protocol version policy. The provisional 0.10.0 bump was removed; runtime canvas capability negotiation remains mandatory.
  • All 6 client mirrors (Rust, Kotlin, Swift, Go, .NET, TypeScript), JSON schemas, and docs regenerated via npm run generate — no generated file was hand-edited.
  • New reducer fixtures (types/test-cases/reducers/280-290) and a new boundary-test suite (types/channels-canvas.test.ts) exercise the new channel's reducer logic and size/depth limits directly, not just the existing coverage-gate shim.
  • Adds docs/.changes/ fragments describing the contract and native-client fixes. No invented release heading or release-time fragment collapse is included.

Validation

  • npm run typecheck
  • npm run lint
  • npm run generate ✅ (clean, idempotent)
  • npm run verify:release-metadata
  • npm run verify:change-fragments
  • npm run verify:generated
  • Node test suite: 476/476 passing, 100% coverage on the types/reducers.ts gate
  • All seven language/check CI jobs pass at 46a47524: check, TypeScript, Swift, Rust, Kotlin, Go and .NET.
  • Follow-up repairs cover missing generated imports/discriminators, Swift reserved words, native reducers and fixture dispatch, lossless canvas snapshots, Go empty-catalog preservation, and single/multi-host mirror revision/isolation behavior.
  • The former changelog/version gate is resolved by removing the unauthorized version bump, not by adding a fake release heading. Only the human PR review policy remains pending.

Open items for reviewers

  • No docs/guide/*.md canvas guide page yet (would need a new per-channel doc-generator function).
  • ChatState intentionally does not carry a parallel canvases list; SessionState.canvases remains the single authoritative catalogue.
  • Renderer choice (VS Code Webviews vs. Integrated Browser) is left open by design — every canvas type in this contract (especially CanvasSourcePresentation) is renderer-neutral and does not require that choice to be resolved.

Introduces a versioned, renderer-neutral `ahp-canvas:` channel for local
extension/package-rendered canvas surfaces, promoting the VS Code PoC
transport into the canonical AHP contract.

- Stable identity: CanvasIdentityKey (chat, source, canvasType, instanceId)
  plus a host-assigned, opaque `incarnation` token that is never reused for
  a superseded logical instance; CanvasPackageSource carries a dedicated
  `sourceId` distinct from display-only `packageName`/`version`.
- Lifecycle state: CanvasTrustState (trusted/pending/blocked) and
  CanvasAvailabilityState (unsupported/notLoaded/loading/empty/ready/failed)
  are independent axes; SessionState.canvases is durable membership,
  distinct from the new read-only listCanvasTypes discovery catalogue.
- Commands: listCanvasTypes (read-only type discovery), openCanvas
  (requestId-scoped idempotency; a new requestId for an already-open
  identity may re-invoke the provider without creating a second identity),
  resolveCanvasSource (read-only current-state/live-URL read, never
  opens/resumes/restarts), invokeCanvasAction (required incarnation
  precondition, no exactly-once-across-crash promise), restartCanvasProvider
  (the only operation that bumps incarnation), and closeCanvas (required
  revision precondition; logical close, distinct from hiding a tab).
- Capability: new server/runtime-side CanvasCapabilities /
  InitializeResult.canvases, independent of the existing client-side
  ClientCapabilities.canvases and of protocol-version negotiation alone.
- Bounds: declared, enforceable size/depth limits (request id length,
  identity field length, schema property/depth, declared-action count,
  input/result length) plus an isCanvasSchemaWithinLimits helper.
- canvasReducer and sessionReducer's session/canvasSet consistently reject
  stale-revision actions rather than special-casing incarnation.

Bumps PROTOCOL_VERSION to 0.10.0 and regenerates all client mirrors (Rust,
Kotlin, Swift, Go, .NET, TypeScript), JSON schemas, and docs via
`npm run generate`. Adds reducer fixtures and boundary tests for the new
channel; CHANGELOG.md heading and fragment collapse remain a release-time
step per RELEASING.md, not part of this change.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add braces to the existing canvas reducer, membership and schema-limit
conditions without changing their behavior or protocol shape. Fix the
canonical sources so downstream generated copies remain reproducible.

Generation, types, lint and the Node coverage suite pass. The existing
release-time changelog heading gate remains unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a069b1a2-65a9-4427-b3fe-6546a3bffc9e
Implement revision-checked reducers and lossless canvas snapshots across native clients. Preserve host isolation, empty catalogues and opaque incarnations, with shared conformance and native mirror regressions. Keep the preview capability-gated on the current protocol version rather than reserving an unauthorized release.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a069b1a2-65a9-4427-b3fe-6546a3bffc9e
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.

1 participant