Skip to content

[WIP] Node SDK canvas launch admission for persisted chats - #2637

Draft
ulugbekna wants to merge 5 commits into
mainfrom
ulugbekna/canvas-sdk-bindings
Draft

ulugbekna wants to merge 5 commits into
mainfrom
ulugbekna/canvas-sdk-bindings

Conversation

@ulugbekna

@ulugbekna ulugbekna commented Sep 12, 2026

Copy link
Copy Markdown

Node canvas launch admission for persisted chats

Approved release boundary

This draft supports canvases in existing, already-persisted chats only.
The caller must establish that the session is already durable through the
ordinary chat/session lifecycle before approving an extension launch. Resuming
an arbitrary session ID or receiving a create/resume response is not itself
proof of durability or canvas-registry readiness.

Canvas-first and zero-turn durability are deferred. This revision removes the
branch-added client.rpc.session.retain(...), session.rpc.retain(),
SessionRetainRequest, session.retained, RetainedEvent, and RetainedData
surfaces, plus their dedicated schema projection, generator machinery, fixtures,
tests, and documentation. It does not replace them with another persistence API.
Ordinary session persistence, save, detach, and resume behavior remains intact.

Launch contract preserved

The SDK still attaches the launch provider before the RPC handshake and requires
an explicit { contractVersion: 1 } acknowledgement. The original source
identity, sessionId, and defaultLaunch remain available. Missing or invalid
acknowledgements fail closed. Absent/null grants, resolver errors, cancellation,
disconnect, and repeated teardown must not produce a late or replayed grant.
The cancellation fix preserves synchronous resolver entry and
original errors.

enableScriptSafety remains a canonical optional initial create/resume setting.
True, false, and omission are forwarded without an SDK default. Read-only command
classification is subject to runtime and managed policy, not a sandbox or policy
override. Cold omission and resident-memory preservation retain their existing
runtime-defined semantics.

Generated contract and provenance

The prior published source head is
861f7794257745126c03bff5ade9070de4a65646. The fetched main input is
cb6fc666cc45175adb11fa9e5021b96d7d37d298, whose source package remains
0.0.0-dev and whose CLI pin is 1.0.89-0. This P3 source revision reconciles
that main through a normal signed merge, without changing dependency pins or
rebasing, amending, force pushing, or bypassing signing/hooks.
Published scope commit: daa5c4fa55b4162ab6f5ad0911dd6afff4cc655b.
Signed fixture-only follow-up/current head:
cb0a1ef84236fdb9f4000274ac1cdae2d9c1eb73.

The launch schema fragments do not depend on retention event definitions.
Generation keeps only seven reviewed launch-v1 API fragments and restores
session-event generation to the unmodified release input. Every fragment matches
the published retention-free runtime source at
ef0ce220610ceedc7243e2eaedaf75ea403248b5
(github/copilot-agent-runtime#20368), including its current descriptions.
Canonical API schema SHA-256:
7f1a7491eb34b4b7552a2af49b993fe3718bb476d3ae241c6842048956126335.

The guarded projection accepts only the recorded released predecessor or the
exact canonical value; unexpected drift fails generation. Explicit schema
arguments remain complete caller-supplied inputs. All six SDK projections and
protocol constants were regenerated, not hand-edited. Only Node RPC output
differs from the fetched main; Node session events and other-language generated
outputs match main. Launch callback cancellation is preserved because it is
required for admission, not because of retention.

The source CLI pin is not a claim that the corresponding release implements
launch-v1 admission. A compatible runtime must acknowledge version 1 before
session startup. Without it, an explicitly configured provider fails closed.
Same-runtime disconnected ownership may remain sticky; replacement refusal is
propagated. No registry-readiness or source-file immutability guarantee is added.

The historical R2, B1/B2/B3, P1, and P2 receipts stay unchanged. They describe
their original source/package/native images, not this narrowed revision.
The separate installed public 1.0.13+B3 carrier is consumer-owned and is not
updated or requalified by this SDK change. A separately built B4 retention-free
backport against public source f13e4a2cc7e4e220974d2333142234e162a3252e
is delivered as private qualification artifacts, not as this PR's source base
or as a package release. It preserves public 1.0.13 metadata, CLI 1.0.83,
the dependency graph, and the B3 client/cancellation implementation.

Validation and publication

P3 validation passed: 348 focused tests across eight files; source/test typecheck;
lint and format checks; ordinary code generation; ESM/CJS/declaration build;
dependency policy; 22 strict public loopback groups per module format; and public
type consumers proving the removed exports, RPC methods, and event listeners
are unavailable. Existing-session resume, initial safety forwarding, startup
negotiation, overlapping cancellation, repeated teardown, reconnect, and
no-late-grant behavior remain covered. Six inherited lint warnings are unchanged.

A supplemental strict compilation of the whole legacy generator still reports
four diagnostics identical to unmodified main; it is not an existing repository
check and no check was disabled. The new schema helper passes strict checking.

Fresh CI exposed two inherited E2E fixture assumptions that conflict with strict
launch admission. The dedicated fake now acknowledges version 1 before issuing
a resolve request and waits for callback completion independently. Unrelated
factory/workflow fixtures no longer opt into the experimental provider and use
the established Node-hosted legacy-runtime resolver. Production negotiation,
generated/package bytes, and B4 are unchanged. The complete affected E2E files
passed locally: 26 tests, with one unchanged inherited skip; no new skips,
live-model traffic, credential use, or fabricated runtime capability.

The initial P3 schema-freshness CI job also exposed an independent main baseline
failure: runtime-artifact.test.mjs passes environmentFile: undefined, causing
the helper to append temporary archive paths to the inherited GITHUB_ENV.
Later Java schema acquisition reads a fixture archive already deleted by test
cleanup. This independently reproduces with the exact unchanged test/helper
bytes from main cb6fc666; it is left unchanged under the bounded scope.
Local all-language generation passed, but CI is not represented as green.

B4 separately passed 94 targeted source tests, source/test typecheck, lint/format,
22 strict public loopback groups per module format, and public declaration
checks. Both complete-image patch routes were applied, checked, reversed, and
reapplied. A fresh exact release export plus the portable source patch, ordinary
generation, and build reproduced all 55 emitted files and modes. Unrelated client
cases were not selected for that bounded run. None of these results relabels
historical B3/native evidence or establishes a new native/platform/model matrix.

The existing PR remains a WIP draft. Current-head CI, ordinary reviewer approval,
compatible-runtime availability, and consumer/native OSS qualification remain
separate gates, recorded in the final receipt. No runtime/VS Code/AHP checkout,
installed carrier, historical receipt, or original PR #2630 was modified.
No release, feature enablement, new PR, or replacement durability mechanism is
part of this task.

Attach the connection-owned launch provider before handshake and require
an explicit v1 acknowledgement before session creation or resume.
Expose global and scoped no-turn retention with canonical null results,
and forward initial script-safety configuration before new extension work.

Make cancellation teardown safe before registration and under overlapping
request/connection cancellation, preserving synchronous resolver errors.
Add focused public loopback coverage and document runtime/release limits.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Preserve both upstream FFI lifecycle test typechecking and the canvas
launch-provider regressions when reconciling the test configuration.
Inherit main's released CLI 1.0.84-5 pin and generated updates unchanged.

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

This comment has been minimized.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generated by SDK Consistency Review Agent for #2637 · copilot · sonnet50 · 85.3 AIC · ⌖ 12.5 AIC · ⊞ 8.3K

Comments that could not be inline-anchored

nodejs/src/types.ts:2611

enableScriptSafety is added here for Node.js session config, and the underlying wire field already exists in the generated RPC types for Go, .NET, Python, Java, and Rust. However, none of those SDKs expose it on their public session-open/resume option types yet (e.g. go/types.go SessionOpenOptions, dotnet/src/Types.cs, python/copilot/client.py kwargs, java/sdk/.../SessionConfig.java + SessionRequestBuilder, rust/src/types.rs). Consider a follow-up to wire this through the other SDKs…

nodejs/src/client.ts:520

The new extensionLaunchProvider connection option (with contractVersion-gated registration, session.retain, cancellation-token propagation, and explicit launch: null deny semantics) is only exposed here in Node.js in this PR. Rust already has an earlier equivalent (ClientOptions::with_extension_launch_provider) but does not yet include these new capabilities, and Go/.NET/Python/Java have no public API surface for registering a launch provider at all (only generated RPC types). If this c…

Check in the reviewed experimental schema fragments and their exact
released predecessor fingerprints. Apply them in ordinary Node codegen
without modifying release inputs or overriding an unexpected new contract.
Keep explicit schema inputs and the other SDK generators unchanged.

Add regression coverage for revision drift, idempotence, source isolation,
and retained-event insertion. Document the Node-only experimental scope
and preserve the compatible-runtime negotiation and release requirements.

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

This comment has been minimized.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generated by SDK Consistency Review Agent for #2637 · copilot · sonnet50 · 126.1 AIC · ⌖ 12.5 AIC · ⊞ 8.3K

Comment thread nodejs/src/types.ts Outdated
Comment thread nodejs/src/types.ts
Merge main cb6fc66 and remove the deferred retention RPC/event projections. Preserve launch-v1 admission, initial script safety and cancellation lifecycle behavior for already-durable chats, with reproducible canonical schema generation.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@ulugbekna ulugbekna changed the title [WIP] Node SDK canvas launch admission and retention [WIP] Node SDK canvas launch admission for persisted chats Sep 23, 2026
@github-actions

This comment has been minimized.

Keep strict version-1 negotiation in the dedicated provider mock. Exercise unrelated factory and workflow tests through the existing Node-hosted legacy runtime path without opting into experimental admission.

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

Copy link
Copy Markdown
Contributor

SDK Consistency Review — PR #2637

Scope of this PR: Only nodejs/ (and the shared scripts/codegen/ generator) are touched. No other language SDK (python/, go/, dotnet/, java/, rust/) is modified.

What changed

  1. Extension launch admission (extensionLaunchProvider) is reworked from a simple registration to a connection-owned admission flow requiring an explicit { contractVersion: 1 } runtime acknowledgement, with per-request cancellation support (ExtensionLaunchProviderConnection in nodejs/src/extensionLaunchProvider.ts) and denies startup on missing/invalid acknowledgement.
  2. A new enableScriptSafety option is added to SessionConfigBase (used by both createSession/resumeSession), wired through to the generated SessionOpenOptions/SessionUpdateOptionsParams wire types.
  3. A schema-revision mechanism (scripts/codegen/canvas-schema.ts + experimental/canvas.schema.json) lets the Node generator apply a reviewed, hash-verified pre-release schema patch (new sessionId/defaultLaunch fields on ExtensionLaunchProviderResolveRequest, new ExtensionLaunchProviderRegistrationResult, nullable launch) ahead of the official CLI schema release.

Cross-SDK findings

  • No inconsistency introduced, and it's self-disclosed. The updated nodejs/README.md explicitly states: "These experimental high-level bindings are currently Node-only... High-level parity in the other SDKs is a separate follow-up." This matches what I verified in the code:

    • enableScriptSafety already exists at the generated wire-type level in Python, Go, .NET, Java, and Rust (SessionOpenOptions/SessionUpdateOptionsParams), since it comes from the shared schema pin, but only the Node SDK's high-level CreateSessionConfig/ResumeSessionConfig surfaces it as an ergonomic option (Java even has a literal null, // enableScriptSafety placeholder in CopilotClient.java).
    • The extended ExtensionLaunchProvider contract (contract-version acknowledgement, sessionId/defaultLaunch/nullable launch on the resolve request/result, per-request cancellation token) is only reflected in the Node SDK. Other SDKs (Go, .NET, Python, Java, Rust) still use the pre-existing, simpler ExtensionLaunchProvider registration/resolve shape without the new fields or contract-version gate, since their generated types come from the released (non-revised) schema.
    • This divergence is because the new fields originate from an unreleased, Node-only "reviewed experimental" schema patch (experimental/canvas.schema.json), applied only by the Node generator; other language generators intentionally stay on the released schema per the PR's own codegen changes.
  • API naming: within the Node SDK, naming is consistent with existing conventions (extensionLaunchProvider, enableScriptSafety, camelCase options), and no naming mismatches were found relative to counterparts that do exist elsewhere (e.g. requestExtensions).

Suggestion (non-blocking)

Since the PR itself flags the other-SDK follow-up as future work, no inline changes are requested. Consider opening a tracking issue for "extension launch admission contract v1 + enableScriptSafety high-level bindings" across Go/.NET/Python/Java/Rust so the documented follow-up isn't lost, especially since the Node-only experimental schema patch will need to be reconciled with the eventual official CLI release schema that the other generators consume.

Conclusion: This PR does not introduce a silent/undocumented parity gap — the Node-only scope is intentional and explicitly documented. No inline review comments needed.

Generated by SDK Consistency Review Agent for #2637 · copilot · sonnet50 · 59.4 AIC · ⌖ 11.8 AIC · ⊞ 8.1K ·

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants