Skip to content

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

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

Port validation and lifecycle fixes to Node, Python, and Go#1341
stephentoub wants to merge 2 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

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>
@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
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>
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 on lines 1118 to 1124
def __init__(
self, session_id: str, client: Any, workspace_path: os.PathLike[str] | str | None = None
self,
session_id: str,
client: Any,
workspace_path: os.PathLike[str] | str | None = None,
on_disconnected: Callable[[CopilotSession], None] | None = None,
):
})

t.Run("should deny tool operations when handler explicitly denies after resume", func(t *testing.T) {
t.Run("should accept explicit deny handler when joining an active session", func(t *testing.T) {
@github-actions
Copy link
Copy Markdown
Contributor

Cross-SDK Consistency Review

This PR does an excellent job porting the C# session lifecycle fixes to Node.js, Python, and Go — the implementations are closely parallel:

Behavior .NET Node.js Python Go Rust
Duplicate active session rejection ✅ (pre-existing) N/A (ownership model)
assertActive guards on session methods N/A
markDisconnected propagation N/A
RPC required-param validation (codegen) ✅ (pre-existing) N/A
Self-unregistration on disconnect N/A
On/on guarded against disconnected session N/A

One gap found: Go's Session.On() is missing an assertActive() guard. Node.js (session.ts), Python (session.py), and .NET all throw/error when on/On is called on a disconnected session. In Go, it silently succeeds (adding a handler to a nil slice, which will never be called). I've left an inline comment on go/session.go with details.

Everything else looks well-aligned. The codegen changes across all three templates (typescript.ts, python.ts, go.ts) are symmetric, and the lifecycle changes in client.go/client.ts/client.py and session.go/session.ts/session.py follow the same structure.

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

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

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