Skip to content

Port validation and lifecycle fixes to Node, Python, and Go#1341

Open
stephentoub wants to merge 9 commits into
mainfrom
stephentoub/port-csharp-validation
Open

Port validation and lifecycle fixes to Node, Python, and Go#1341
stephentoub wants to merge 9 commits into
mainfrom
stephentoub/port-csharp-validation

Conversation

@stephentoub
Copy link
Copy Markdown
Collaborator

C# recently tightened generated RPC argument validation and session lifecycle cleanup. This ports the commensurate behavior to the SDKs that had matching gaps so clients fail earlier, do not keep stale active sessions around, and reject duplicate active local sessions consistently.

Summary

  • Updates the TypeScript, Python, and Go RPC generators so generated session APIs validate required params before sending and reject session-scoped RPC calls after disconnect.
  • Cleans up Node, Python, and Go session/client lifecycle handling: direct disconnect unregisters from the parent client, stop/force-stop/delete paths clear active session maps safely, and duplicate active session IDs are rejected.
  • Adds focused lifecycle and generated-RPC regression coverage, plus adjusts Go e2e resume coverage for the new duplicate-active-session contract.

Rust was reviewed but left unchanged because its existing ownership/lifecycle model already covers the comparable behavior.

Validation

  • cd nodejs && npm run typecheck
  • cd nodejs && npm test -- client.test.ts --testNamePattern "directly disconnected|reports stop errors|replacement session|duplicate active|validates required"
  • cd python && python -m pytest -q test_client.py test_rpc_generated.py
  • cd python && python -m ruff check test_client.py test_rpc_generated.py copilot\client.py copilot\session.py
  • cd go && go test -timeout 20m ./...
  • git diff --check

@stephentoub stephentoub requested a review from a team as a code owner May 20, 2026 03:07
Copilot AI review requested due to automatic review settings May 20, 2026 03:07
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR ports stricter generated-RPC argument validation and session lifecycle cleanup (previously tightened in C#) to the Node.js, Python, and Go SDKs, so clients fail earlier on invalid usage and don’t retain stale/duplicate active sessions.

Changes:

  • Updated TS/Python/Go RPC generators (and regenerated outputs) to validate required request objects before sending and to reject session-scoped RPC calls once a session is disconnected.
  • Hardened session/client lifecycle cleanup across Node/Python/Go (disconnect unregisters from the owning client; stop/force-stop/delete ensure sessions are marked unusable; duplicate active session IDs are rejected).
  • Added/updated targeted unit + E2E regression coverage to lock in lifecycle/validation behavior, including updated Go resume tests for the new “duplicate active session” contract.
Show a summary per file
File Description
scripts/codegen/typescript.ts Emit session “active” assertions and required-params checks in generated TS RPC methods.
scripts/codegen/python.ts Emit session “active” assertions and required-params checks in generated Python RPC methods.
scripts/codegen/go.ts Emit session “active” assertions and required-params checks in generated Go RPC methods.
python/test_rpc_generated.py Adds regression tests for required-params validation and active-check behavior.
python/test_client.py Adds/updates lifecycle tests for disconnect/unregister, duplicates, and failure cleanup paths.
python/copilot/session.py Implements disconnect idempotency + active-use rejection; clears handlers/hooks on disconnect and supports unregister callback.
python/copilot/generated/rpc.py Regenerated RPC surface with params validation and assert-active hooks for session APIs.
python/copilot/client.py Adds session registration helpers; enforces duplicate-active rejection and consistent cleanup on stop/force-stop/delete.
nodejs/test/client.test.ts Adds unit coverage for lifecycle cleanup, stop errors, duplicate registration, and params validation.
nodejs/src/session.ts Adds disconnect state, active assertions, idempotent disconnect, forced-disconnect hook, and unregister callback.
nodejs/src/generated/rpc.ts Regenerated RPC surface with params validation and assert-active hooks for session APIs.
nodejs/src/client.ts Enforces duplicate-active session IDs; ensures stop/force-stop/delete mark sessions disconnected and clears internal RPC state.
go/session.go Adds session active-state tracking, idempotent Disconnect, and client unregister on disconnect; rejects usage after disconnect.
go/session_test.go Adds unit tests covering lifecycle cleanup and duplicate-active session registration behavior.
go/rpc/zrpc.go Regenerated Go RPC surface with params validation and assert-active checks for session APIs.
go/rpc/generated_rpc_api_shape_test.go Adds regression tests for required-params validation and active-check ordering.
go/internal/e2e/session_e2e_test.go Updates E2E expectations for post-disconnect behavior and validates duplicate-active resume rejection; adds resume-client helper.
go/internal/e2e/session_config_e2e_test.go Updates resume scenarios to use a separate TCP client consistent with the new “active session” contract.
go/internal/e2e/permissions_e2e_test.go Updates resume permissions scenario to use separate resume client and aligns with new join behavior.
go/internal/e2e/mcp_and_agents_e2e_test.go Updates resume scenarios to use separate resume client (TCP mode).
go/internal/e2e/commands_and_elicitation_e2e_test.go Updates resume scenario to use separate resume client (TCP mode).
go/client.go Enforces duplicate-active session IDs and centralizes session snapshot/clear helpers; ensures stop/force-stop disconnects and marks sessions unusable.

Copilot's findings

Files not reviewed (1)
  • go/rpc/zrpc.go: Language not supported
  • Files reviewed: 19/22 changed files
  • Comments generated: 2

Comment thread python/copilot/session.py
Comment thread go/internal/e2e/permissions_e2e_test.go Outdated
@github-actions

This comment has been minimized.

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

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 issue #1341 · ● 2.6M

Copilot AI added 3 commits May 20, 2026 08:38
Port C# argument validation and lifecycle cleanup behavior to Node, Python, and Go. Generated session RPC APIs now validate required params and reject use after disconnect, while clients clean up active session maps consistently and reject duplicate active session IDs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Apply ruff formatting to the new Python lifecycle regression tests so CI format checks pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@stephentoub stephentoub force-pushed the stephentoub/port-csharp-validation branch from bac3c1c to ce8aa9e Compare May 20, 2026 13:25
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

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

This comment has been minimized.

Mark Python test sessions inactive before same-client resume so the duplicate active-session guard is preserved without destroying the server-side session.

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

This comment has been minimized.

…rp-validation

# Conflicts:
#	python/e2e/test_mcp_and_agents_e2e.py
@github-actions

This comment has been minimized.

Copilot AI added 2 commits May 20, 2026 23:15
…rp-validation

# Conflicts:
#	nodejs/src/generated/rpc.ts
#	python/copilot/generated/rpc.py
@github-actions
Copy link
Copy Markdown
Contributor

Cross-SDK Consistency Review ✅

This PR ports C# validation and lifecycle fixes to Node.js, Python, and Go. I reviewed the changes across all five SDK implementations for consistency. Overall, the PR is well-executed and brings the three modified SDKs into alignment with the .NET baseline.

Verification Summary

Feature .NET (source) Node.js Python Go Rust
Duplicate active session rejection RegisterSessionTryAdd throws registerSession → throws if different object _register_session → throws if different object registerSession → returns error if different object N/A — ownership prevents duplicates
Session-scoped RPC rejects after disconnect _session.ThrowIfDisposed() in generated Rpc.cs assertActive?.() in generated rpc.ts assert_active() in generated rpc.py assertActive in zrpc.go N/A — ownership model covers this
Session methods reject after disconnect ThrowIfDisposed() in send/on/rpc/ui assertActive() in send/on/rpc/ui _assert_not_destroyed() in send/on/rpc/ui assertActive() in Send/SendAndWait N/A
Session disconnect unregisters from parent client RemoveFromClient() in DisposeAsync onDisconnected callback _on_disconnected callback owner field + unregisterSession Drop impl calls unregister_session
Stop clears sessions safely (snapshot then clear) Snapshot via _sessions.Values.ToArray() [...this.sessions.values()] snapshot list(self._sessions.values()) snapshot snapshotSessions() helper ✅ router snapshot

One Minor Observation (pre-existing .NET gap, not introduced here)

.NET's ForceStopAsync only calls _sessions.Clear() without marking the cleared sessions as disposed — it relies on the transport closure to cause ObjectDisposedException on any subsequent RPC calls. By contrast, this PR's additions to Node, Python, and Go explicitly call _markDisconnected() / session.markDisconnected() on sessions cleared by force_stop. This is strictly a net improvement (cleaner error messages) rather than a consistency problem. If desired, .NET's ForceStopAsync could similarly mark sessions disposed, but that's a pre-existing gap outside this PR's scope.

Retry Logic Removal (Node.js stop())

Node's old stop() had a 3-attempt exponential backoff not present in other SDKs. Removing it aligns Node with .NET, Python, and Go — ✅ correct.

Rust

The PR's assertion that Rust's existing ownership/lifecycle model covers the comparable behavior is correct: Session is returned by value (!Clone), Drop unregisters via unregister_session, and router.register() overwrites a prior entry only when a new Session value is created (which can't coexist with a live prior one). No change needed there.


The changes are consistent across all languages. No cross-SDK gaps introduced by this PR.

Generated by SDK Consistency Review Agent for issue #1341 · ● 1.3M ·

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.

3 participants