Skip to content

Add session fs support across SDKs#1036

Merged
SteveSandersonMS merged 9 commits intomainfrom
stevesa/sessionfs-for-all
Apr 7, 2026
Merged

Add session fs support across SDKs#1036
SteveSandersonMS merged 9 commits intomainfrom
stevesa/sessionfs-for-all

Conversation

@SteveSandersonMS
Copy link
Copy Markdown
Contributor

Summary

  • add generated client-session/session-fs plumbing for the .NET SDK and wire it through client and session configuration
  • add session-fs support and shared-snapshot E2E coverage for the .NET, Go, and Python SDKs
  • tighten session-fs config handling across SDKs so required fields must be supplied explicitly

Testing

  • dotnet test test\GitHub.Copilot.SDK.Test.csproj --filter "FullyQualifiedName~SessionFsTests"
  • go test . -run "TestClient_(URLParsing|SessionFsConfig)"
  • python -m pytest test_client.py -k "session_fs or SessionFs" -q
  • npm test -- --run test\client.test.ts

SteveSandersonMS and others added 2 commits April 7, 2026 16:18
Co-Authored-By: SteveSandersonMS <1101362+SteveSandersonMS@users.noreply.github.com>
Co-Authored-By: SteveSandersonMS <1101362+SteveSandersonMS@users.noreply.github.com>
@github-actions

This comment has been minimized.

SteveSandersonMS and others added 3 commits April 7, 2026 17:08
Co-Authored-By: SteveSandersonMS <1101362+SteveSandersonMS@users.noreply.github.com>
Co-Authored-By: SteveSandersonMS <1101362+SteveSandersonMS@users.noreply.github.com>
Co-Authored-By: SteveSandersonMS <1101362+SteveSandersonMS@users.noreply.github.com>
@github-actions

This comment has been minimized.

SteveSandersonMS and others added 2 commits April 7, 2026 17:33
Co-Authored-By: SteveSandersonMS <1101362+SteveSandersonMS@users.noreply.github.com>
Co-Authored-By: SteveSandersonMS <1101362+SteveSandersonMS@users.noreply.github.com>
@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 #1036

ElicitationResult,
InputOptions,
SessionCapabilities,
SessionFsConfig,
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.

Suggestion: also export CreateSessionFsHandler (and SessionFsHandler) for parity with other SDKs

SessionFsConfig is now exported here so users can configure the feature, but to actually implement a session-fs handler users also need the handler types. Currently those aren't accessible through the public API:

  • SessionFsHandler — the Protocol class in generated/rpc.py that users implement
  • CreateSessionFsHandler — the type alias Callable[["CopilotSession"], SessionFsHandler]

Compare with the other SDKs:

  • Node.js index.ts exports SessionFsHandler alongside SessionFsConfig
  • .NET ISessionFsHandler is public in Generated/Rpc.cs
  • Go rpc.SessionFsHandler is accessible via the rpc sub-package

The existing precedent in Python is that analogous callback types like ElicitationHandler are exported from __init__.py. Without CreateSessionFsHandler (or at minimum SessionFsHandler) being exported, users would need to reach into copilot.session or copilot.generated.rpc to type their handler, which isn't great for a public API.

Suggested addition to the from .session import (...) block and __all__:

CreateSessionFsHandler,
SessionFsHandler,        # re-exported from generated/rpc via session.py

(Or export SessionFsHandler directly from generated/rpc if that's the preferred pattern.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch. I exported both CreateSessionFsHandler and SessionFsHandler from copilot.__init__ so the public Python API exposes the handler types alongside SessionFsConfig.

@SteveSandersonMS SteveSandersonMS marked this pull request as ready for review April 7, 2026 17:23
@SteveSandersonMS SteveSandersonMS requested a review from a team as a code owner April 7, 2026 17:23
Copilot AI review requested due to automatic review settings April 7, 2026 17:23
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

Adds “session fs” support across the SDKs by introducing client-session RPC handler plumbing (server→client calls) and wiring connection/session configuration so the Copilot CLI can delegate session-scoped filesystem operations back to the SDK host.

Changes:

  • Extend codegen (Go/Python/.NET) to include clientSession RPC surface + registration helpers for server→client calls (e.g., sessionFs.*).
  • Add/strengthen session-fs configuration plumbing + validation (notably Node/Python/Go) and require per-session handler factories when enabled.
  • Add cross-SDK E2E and unit coverage for session-fs behavior (route I/O, resume, large output temp files, compaction, and setProvider rejection).
Show a summary per file
File Description
scripts/codegen/python.ts Includes clientSession methods in Python RPC generation and emits client-session handler registration code.
scripts/codegen/go.ts Includes clientSession methods in Go RPC generation and emits client-session handler registration code.
scripts/codegen/csharp.ts Adds client-session handler generation/registration and improves RPC type naming helpers.
python/test_client.py Adds unit tests asserting session-fs config validation errors.
python/e2e/test_session_fs.py Adds Python E2E coverage for session-fs behaviors.
python/copilot/session.py Introduces SessionFsConfig/handler types and session config fields for session-fs.
python/copilot/generated/rpc.py Adds generated SessionFs RPC param/result types + client-session handler protocol/registration.
python/copilot/client.py Wires session-fs config into client start + session creation/resume with required handler factory.
python/copilot/init.py Re-exports SessionFsConfig.
nodejs/test/client.test.ts Adds Node unit tests for session-fs config validation.
nodejs/src/client.ts Adds session-fs config validation in the Node client constructor.
go/types.go Adds Go SessionFsConfig and per-session handler factory options.
go/session.go Adds per-session client-session handler container.
go/rpc/generated_rpc.go Adds generated SessionFs RPC types + client-session handler registration support.
go/internal/e2e/testharness/context.go Adjusts CI fake-token injection to avoid affecting external-server clients.
go/internal/e2e/session_fs_test.go Adds Go E2E coverage for session-fs behaviors.
go/client.go Adds session-fs config validation, provider registration on connect, and per-session handler wiring.
go/client_test.go Adds Go unit tests for session-fs config validation.
dotnet/test/SessionFsTests.cs Adds .NET E2E coverage for session-fs behaviors.
dotnet/test/Harness/E2ETestContext.cs Allows passing options into harness client creation; refines CI fake-token behavior.
dotnet/src/Types.cs Adds SessionFsConfig to options and per-session handler factory hooks to session configs.
dotnet/src/Session.cs Adds per-session client-session handler container.
dotnet/src/Generated/Rpc.cs Adds generated SessionFs RPC types + client-session handler registration types.
dotnet/src/Client.cs Registers session-fs provider on connect, wires per-session session-fs handlers, and registers client-session RPC handlers.

Copilot's findings

  • Files reviewed: 22/24 changed files
  • Comments generated: 3

Comment on lines +1091 to +1094
await Rpc.SessionFs.SetProviderAsync(
_options.SessionFs.InitialCwd,
_options.SessionFs.SessionStatePath,
_options.SessionFs.Conventions,
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

ConfigureSessionFsAsync forwards SessionFs values directly to sessionFs.setProvider without validating required fields (non-empty InitialCwd/SessionStatePath) or guarding invalid enum values. Other SDKs in this PR validate early and throw a clear configuration error; adding equivalent validation here would prevent late/opaque failures from the RPC call.

Suggested change
await Rpc.SessionFs.SetProviderAsync(
_options.SessionFs.InitialCwd,
_options.SessionFs.SessionStatePath,
_options.SessionFs.Conventions,
var sessionFs = _options.SessionFs;
if (string.IsNullOrWhiteSpace(sessionFs.InitialCwd))
{
throw new InvalidOperationException(
"CopilotClientOptions.SessionFs.InitialCwd is required when CopilotClientOptions.SessionFs is configured.");
}
if (string.IsNullOrWhiteSpace(sessionFs.SessionStatePath))
{
throw new InvalidOperationException(
"CopilotClientOptions.SessionFs.SessionStatePath is required when CopilotClientOptions.SessionFs is configured.");
}
object? conventions = sessionFs.Conventions;
if (conventions is null || !System.Enum.IsDefined(conventions.GetType(), conventions))
{
throw new InvalidOperationException(
$"CopilotClientOptions.SessionFs.Conventions has an invalid value '{sessionFs.Conventions}'.");
}
await Rpc.SessionFs.SetProviderAsync(
sessionFs.InitialCwd,
sessionFs.SessionStatePath,
sessionFs.Conventions,

Copilot uses AI. Check for mistakes.


def provider_path(provider_root: Path, session_id: str, path: str) -> Path:
return provider_root / session_id / path.lstrip("/")
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

provider_path directly concatenates the server-supplied path onto provider_root/session_id without normalizing or preventing .. segments. A malformed path could escape the provider root and read/write outside the test sandbox. Consider resolving the full path and asserting it stays within the expected session root (similar to the .NET test’s ResolvePath guard).

Suggested change
return provider_root / session_id / path.lstrip("/")
session_root = (provider_root / session_id).resolve()
resolved_path = (session_root / path.lstrip("/")).resolve()
try:
resolved_path.relative_to(session_root)
except ValueError as exc:
raise ValueError(f"Session FS path escapes provider root: {path}") from exc
return resolved_path

Copilot uses AI. Check for mistakes.
Comment on lines +389 to +393
func providerPath(root string, sessionID string, path string) string {
trimmed := strings.TrimPrefix(path, "/")
if trimmed == "" {
return filepath.Join(root, sessionID)
}
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

providerPath uses filepath.Join on a server-supplied path without validating for .. segments; filepath.Join will clean the path and can escape root/sessionID (and even root) if path contains traversal. To keep the E2E provider sandboxed, consider cleaning/resolving and asserting the result remains under the expected session directory.

Copilot uses AI. Check for mistakes.
@github-actions

This comment has been minimized.

Co-Authored-By: SteveSandersonMS <1101362+SteveSandersonMS@users.noreply.github.com>
@SteveSandersonMS SteveSandersonMS merged commit 8569d92 into main Apr 7, 2026
33 checks passed
@SteveSandersonMS SteveSandersonMS deleted the stevesa/sessionfs-for-all branch April 7, 2026 17:48
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 7, 2026

Cross-SDK Consistency Review ✅ (one minor suggestion)

This PR adds session-fs support across all four SDKs (Node.js, Python, Go, .NET) — great feature parity! The overall API design is consistent:

Concern Node.js Python Go .NET
Client option sessionFs?: SessionFsConfig session_fs: SessionFsConfig | None SessionFs *SessionFsConfig SessionFs: SessionFsConfig?
Session-level handler factory createSessionFsHandler in SessionConfig create_session_fs_handler param CreateSessionFsHandler in SessionConfig CreateSessionFsHandler in SessionConfig
Handler interface SessionFsHandler SessionFsHandler (Protocol) SessionFsHandler (interface) ISessionFsHandler
Validation (required when fs configured) ✅ throws ✅ throws ✅ returns error ✅ throws

One minor naming inconsistency

I left an inline comment on dotnet/src/Types.cs:248 about SessionFsConfig.Conventions using the generated type SessionFsSetProviderRequestConventions. The other SDKs expose a cleaner user-facing type for this property (e.g., Python's SessionFsConventions, Go's rpc.Conventions, Node.js's "windows" | "posix" union). The suggestion is to define a SessionFsConventions enum in Types.cs to avoid leaking the internal RPC method name into the public API.

Everything else looks well-aligned across SDKs. Nice work bringing all four to parity!

Generated by SDK Consistency Review Agent for issue #1036 ·

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 #1036

/// <summary>
/// Path conventions used by this filesystem provider.
/// </summary>
public required SessionFsSetProviderRequestConventions Conventions { get; init; }
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.

Cross-SDK consistency suggestion: The type SessionFsSetProviderRequestConventions comes from the generated RPC layer and leaks the internal RPC method name (sessionFs.setProvider) into the public API surface.

The other SDKs define a dedicated user-facing type for this:

  • Node.js: "windows" | "posix" string union (in SessionFsConfig)
  • Python: SessionFsConventions = Literal["posix", "windows"]
  • Go: rpc.Conventions (the type is named simply Conventions, not SessionFsSetProviderRequestConventions)

Consider defining a clean public alias in Types.cs, e.g.:

/// <summary>Path-convention options for a session filesystem provider.</summary>
public enum SessionFsConventions
{
    [JsonStringEnumMemberName("posix")]
    Posix,
    [JsonStringEnumMemberName("windows")]
    Windows,
}

…and using SessionFsConventions in SessionFsConfig.Conventions instead. The mapping to the internal SessionFsSetProviderRequestConventions can live inside ConfigureSessionFsAsync in Client.cs. This keeps the public API surface free of generated-type names.

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.

2 participants