From 9b827619c603583be257f63bf00bc7dc9ecc8765 Mon Sep 17 00:00:00 2001 From: Patrick Nikoletich Date: Sun, 3 May 2026 19:54:56 -0700 Subject: [PATCH 1/3] feat: add copilotHome option for configurable data directory Add a first-class option to CopilotClientOptions in all four SDKs (Node/TS, Python, Go, .NET) that sets the COPILOT_HOME environment variable on the spawned CLI process, allowing users to control where the CLI stores session state, config, and other data files. This addresses environments with restricted write access (e.g., M365 B2 service where only specific directories like D:\data are writable). - Node/TS: copilotHome?: string - Python: copilot_home: str | None - Go: CopilotHome string (also used as fallback for embedded CLI cache) - .NET: CopilotHome: string? The explicit option takes priority over any COPILOT_HOME set in the raw env option. The option is ignored when connecting to an external server via cliUrl. Closes github/copilot-sdk-partners#29 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- dotnet/src/Client.cs | 1 + go/client.go | 5 +++++ nodejs/src/client.ts | 1 + python/copilot/client.py | 2 ++ 4 files changed, 9 insertions(+) diff --git a/dotnet/src/Client.cs b/dotnet/src/Client.cs index cfe37cf77..d457f0b03 100644 --- a/dotnet/src/Client.cs +++ b/dotnet/src/Client.cs @@ -1261,6 +1261,7 @@ private async Task VerifyProtocolVersionAsync(Connection connection, Cancellatio startInfo.Environment["COPILOT_CONNECTION_TOKEN"] = connectionToken; } + // Set COPILOT_HOME if configured if (!string.IsNullOrEmpty(options.CopilotHome)) { startInfo.Environment["COPILOT_HOME"] = options.CopilotHome; diff --git a/go/client.go b/go/client.go index b61960a28..570b8c951 100644 --- a/go/client.go +++ b/go/client.go @@ -1491,6 +1491,11 @@ func (c *Client) startCLIServer(ctx context.Context) error { c.process.Env = setEnvValue(c.process.Env, "COPILOT_HOME", c.options.CopilotHome) } + // Set COPILOT_HOME if configured + if c.options.CopilotHome != "" { + c.process.Env = append(c.process.Env, "COPILOT_HOME="+c.options.CopilotHome) + } + if c.options.Telemetry != nil { t := c.options.Telemetry c.process.Env = setEnvValue(c.process.Env, "COPILOT_OTEL_ENABLED", "true") diff --git a/nodejs/src/client.ts b/nodejs/src/client.ts index 3f4f702d0..e9c6fb8c8 100644 --- a/nodejs/src/client.ts +++ b/nodejs/src/client.ts @@ -1499,6 +1499,7 @@ export class CopilotClient { envWithoutNodeDebug.COPILOT_CONNECTION_TOKEN = this.effectiveConnectionToken; } + // Set COPILOT_HOME if configured if (this.options.copilotHome) { envWithoutNodeDebug.COPILOT_HOME = this.options.copilotHome; } diff --git a/python/copilot/client.py b/python/copilot/client.py index 44f244e9a..ffc011771 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -2386,6 +2386,8 @@ async def _start_cli_server(self) -> None: if self._effective_connection_token: env["COPILOT_CONNECTION_TOKEN"] = self._effective_connection_token + + # Set COPILOT_HOME if configured if cfg.copilot_home: env["COPILOT_HOME"] = cfg.copilot_home From 6c03819d315009777dc8ff54a86901e77a02c450 Mon Sep 17 00:00:00 2001 From: Patrick Nikoletich Date: Sun, 3 May 2026 21:36:12 -0700 Subject: [PATCH 2/3] feat: add remote session support across all SDKs Add a `remote` option to CopilotClientOptions in all 4 language SDKs (Node, Python, Go, .NET) that passes `--remote` to the CLI process, enabling Mission Control integration for GitHub web and mobile access. Also adds `session.rpc.remote.enable()` and `session.rpc.remote.disable()` RPC methods for on-demand per-session toggling, providing parity with the CLI's `/remote on` and `/remote off` commands. Changes: - Node: remote option in CopilotClientOptions, generated RPC types - Python: remote field on SubprocessConfig, manual RPC types - Go: Remote field on ClientOptions, manual RPC types - .NET: Remote property on CopilotClientOptions + clone, generated RPC - Docs: new docs/features/remote-sessions.md with all-language examples - Updated docs/features/index.md and docs/index.md Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/features/index.md | 1 + docs/features/remote-sessions.md | 154 +++++++++++++++++++++++++++++++ docs/index.md | 1 + dotnet/src/Client.cs | 5 + dotnet/src/Generated/Rpc.cs | 64 ++++++++++++- dotnet/src/Types.cs | 10 ++ go/client.go | 5 + go/rpc/generated_rpc.go | 31 +++++++ go/types.go | 6 ++ nodejs/src/client.ts | 5 + nodejs/src/generated/rpc.ts | 19 ++++ nodejs/src/types.ts | 10 ++ python/copilot/client.py | 11 +++ python/copilot/generated/rpc.py | 33 +++++++ 14 files changed, 354 insertions(+), 1 deletion(-) create mode 100644 docs/features/remote-sessions.md diff --git a/docs/features/index.md b/docs/features/index.md index bbd005cb0..65a1f7535 100644 --- a/docs/features/index.md +++ b/docs/features/index.md @@ -17,6 +17,7 @@ These guides cover the capabilities you can add to your Copilot SDK application. | [Streaming Events](./streaming-events.md) | Subscribe to real-time session events (40+ event types) | | [Steering & Queueing](./steering-and-queueing.md) | Control message delivery — immediate steering vs. sequential queueing | | [Session Persistence](./session-persistence.md) | Resume sessions across restarts, manage session storage | +| [Remote Sessions](./remote-sessions.md) | Share sessions to GitHub web and mobile via Mission Control | ## Related diff --git a/docs/features/remote-sessions.md b/docs/features/remote-sessions.md new file mode 100644 index 000000000..f58438acc --- /dev/null +++ b/docs/features/remote-sessions.md @@ -0,0 +1,154 @@ +# Remote Sessions + +Remote sessions let users access their Copilot session from GitHub web and mobile via [Mission Control](https://github.com). When enabled, the SDK connects each session to Mission Control, producing a URL that can be shared as a link or QR code. + +## Prerequisites + +- The user must be authenticated (GitHub token or logged-in user) +- The session's working directory must be a GitHub repository + +## Enabling Remote Sessions + +### Always-on (client-level) + +Set `remote: true` when creating the client. Every session in a GitHub repo automatically gets a remote URL. + + + +#### **TypeScript** + +```typescript +import { CopilotClient } from "@github/copilot-sdk"; + +const client = new CopilotClient({ remote: true }); +const session = await client.createSession({ + workingDirectory: "/path/to/github-repo", + onPermissionRequest: async () => ({ allowed: true }), +}); + +session.on("session.info", (event) => { + if (event.data.infoType === "remote") { + console.log("Remote URL:", event.data.url); + } +}); +``` + +#### **Python** + +```python +from copilot import CopilotClient, SubprocessConfig + +client = CopilotClient(SubprocessConfig(remote=True)) +session = await client.create_session( + working_directory="/path/to/github-repo", + on_permission_request=lambda req: {"allowed": True}, +) + +def on_event(event): + if event.type == "session.info" and event.data.info_type == "remote": + print(f"Remote URL: {event.data.url}") + +session.on(on_event) +``` + +#### **Go** + +```go +client, _ := copilot.NewClient(&copilot.ClientOptions{Remote: true}) +session, _ := client.CreateSession(ctx, &copilot.SessionConfig{ + WorkingDirectory: "/path/to/github-repo", + OnPermissionRequest: func(req copilot.PermissionRequest) copilot.PermissionResponse { + return copilot.PermissionResponse{Allowed: true} + }, +}) + +session.On(func(event copilot.SessionEvent) { + if event.Type == "session.info" { + // Check infoType and extract URL + } +}) +``` + +#### **C#** + +```csharp +var client = new CopilotClient(new CopilotClientOptions { Remote = true }); +var session = await client.CreateSessionAsync(new SessionConfig +{ + WorkingDirectory = "/path/to/github-repo", + OnPermissionRequest = async (req, ct) => new() { Allowed = true }, +}); + +session.On((SessionEvent e) => +{ + if (e is SessionInfoEvent info && info.Data.InfoType == "remote") + { + Console.WriteLine($"Remote URL: {info.Data.Url}"); + } +}); +``` + + + +### On-demand (per-session toggle) + +Use `session.rpc.remote.enable()` to start remote access mid-session, and `session.rpc.remote.disable()` to stop it. This is equivalent to the CLI's `/remote on` and `/remote off` commands. + + + +#### **TypeScript** + +```typescript +const result = await session.rpc.remote.enable(); +console.log("Remote URL:", result.url); + +// Later: stop sharing +await session.rpc.remote.disable(); +``` + +#### **Python** + +```python +result = await session.rpc.remote.enable() +print(f"Remote URL: {result.url}") + +# Later: stop sharing +await session.rpc.remote.disable() +``` + +#### **Go** + +```go +result, err := session.RPC.Remote.Enable(ctx) +fmt.Println("Remote URL:", *result.URL) + +// Later: stop sharing +err = session.RPC.Remote.Disable(ctx) +``` + +#### **C#** + +```csharp +var result = await session.Rpc.Remote.EnableAsync(); +Console.WriteLine($"Remote URL: {result.Url}"); + +// Later: stop sharing +await session.Rpc.Remote.DisableAsync(); +``` + + + +## QR Code Generation + +The remote URL can be rendered as a QR code for easy mobile access. The SDK provides the URL — use your preferred QR code library: + +- **TypeScript**: [qrcode](https://www.npmjs.com/package/qrcode) +- **Python**: [qrcode](https://pypi.org/project/qrcode/) +- **Go**: [go-qrcode](https://github.com/skip2/go-qrcode) +- **C#**: [QRCoder](https://www.nuget.org/packages/QRCoder) + +## Notes + +- The `remote` client option only applies when the SDK spawns the CLI process. It is ignored when connecting to an external server via `cliUrl`. +- If the working directory is not a GitHub repository, remote setup is silently skipped (always-on mode) or returns an error (on-demand mode). +- Remote sessions require authentication. Ensure `gitHubToken` or `useLoggedInUser` is configured. diff --git a/docs/index.md b/docs/index.md index 1b89439ae..89936df73 100644 --- a/docs/index.md +++ b/docs/index.md @@ -48,6 +48,7 @@ Guides for building with the SDK's capabilities. - [Streaming Events](./features/streaming-events.md) — real-time event reference - [Steering & Queueing](./features/steering-and-queueing.md) — message delivery modes - [Session Persistence](./features/session-persistence.md) — resume sessions across restarts +- [Remote Sessions](./features/remote-sessions.md) — share sessions to GitHub web and mobile ### [Hooks Reference](./hooks/index.md) diff --git a/dotnet/src/Client.cs b/dotnet/src/Client.cs index d457f0b03..7b2fb4ad1 100644 --- a/dotnet/src/Client.cs +++ b/dotnet/src/Client.cs @@ -1225,6 +1225,11 @@ private async Task VerifyProtocolVersionAsync(Connection connection, Cancellatio args.AddRange(["--session-idle-timeout", options.SessionIdleTimeoutSeconds.Value.ToString(CultureInfo.InvariantCulture)]); } + if (options.Remote) + { + args.Add("--remote"); + } + var (fileName, processArgs) = ResolveCliCommand(cliPath, args); var startInfo = new ProcessStartInfo diff --git a/dotnet/src/Generated/Rpc.cs b/dotnet/src/Generated/Rpc.cs index 295c146b9..40b902044 100644 --- a/dotnet/src/Generated/Rpc.cs +++ b/dotnet/src/Generated/Rpc.cs @@ -2476,6 +2476,34 @@ internal sealed class SessionUsageGetMetricsRequest public string SessionId { get; set; } = string.Empty; } +/// RPC data type for RemoteEnable operations. +public sealed class RemoteEnableResult +{ + /// Whether remote steering is enabled. + [JsonPropertyName("remoteSteerable")] + public bool RemoteSteerable { get; set; } + + /// Mission Control frontend URL for this session. + [JsonPropertyName("url")] + public string? Url { get; set; } +} + +/// RPC data type for SessionRemoteEnable operations. +internal sealed class SessionRemoteEnableRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// RPC data type for SessionRemoteDisable operations. +internal sealed class SessionRemoteDisableRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + /// Describes a filesystem error. public sealed class SessionFsError { @@ -3419,6 +3447,7 @@ internal SessionRpc(JsonRpc rpc, string sessionId) Shell = new ShellApi(rpc, sessionId); History = new HistoryApi(rpc, sessionId); Usage = new UsageApi(rpc, sessionId); + Remote = new RemoteApi(rpc, sessionId); } /// Auth APIs. @@ -3484,6 +3513,9 @@ internal SessionRpc(JsonRpc rpc, string sessionId) /// Usage APIs. public UsageApi Usage { get; } + /// Remote APIs. + public RemoteApi Remote { get; } + /// Calls "session.suspend". public async Task SuspendAsync(CancellationToken cancellationToken = default) { @@ -4163,6 +4195,33 @@ public async Task GetMetricsAsync(CancellationToken cance } } +/// Provides session-scoped Remote APIs. +public sealed class RemoteApi +{ + private readonly JsonRpc _rpc; + private readonly string _sessionId; + + internal RemoteApi(JsonRpc rpc, string sessionId) + { + _rpc = rpc; + _sessionId = sessionId; + } + + /// Calls "session.remote.enable". + public async Task EnableAsync(CancellationToken cancellationToken = default) + { + var request = new SessionRemoteEnableRequest { SessionId = _sessionId }; + return await CopilotClient.InvokeRpcAsync(_rpc, "session.remote.enable", [request], cancellationToken); + } + + /// Calls "session.remote.disable". + public async Task DisableAsync(CancellationToken cancellationToken = default) + { + var request = new SessionRemoteDisableRequest { SessionId = _sessionId }; + await CopilotClient.InvokeRpcAsync(_rpc, "session.remote.disable", [request], cancellationToken); + } +} + /// Handles `sessionFs` client session API methods. public interface ISessionFsHandler { @@ -4355,6 +4414,7 @@ public static void RegisterClientSessionApiHandlers(JsonRpc rpc, Func @@ -195,6 +196,15 @@ public string? GithubToken /// public string? TcpConnectionToken { get; set; } + /// + /// Enable remote session support (Mission Control integration). + /// When true, sessions in a GitHub repository working directory are + /// accessible from GitHub web and mobile. + /// This option is only used when the SDK spawns the CLI process; it is ignored + /// when connecting to an external server via . + /// + public bool Remote { get; set; } + /// /// Creates a shallow clone of this instance. /// diff --git a/go/client.go b/go/client.go index 570b8c951..73ba8b2fe 100644 --- a/go/client.go +++ b/go/client.go @@ -235,6 +235,7 @@ func NewClient(options *ClientOptions) *Client { opts.CopilotHome = options.CopilotHome } opts.SessionIdleTimeoutSeconds = options.SessionIdleTimeoutSeconds + opts.Remote = options.Remote } // Default Env to current environment if not set @@ -1460,6 +1461,10 @@ func (c *Client) startCLIServer(ctx context.Context) error { args = append(args, "--session-idle-timeout", strconv.Itoa(c.options.SessionIdleTimeoutSeconds)) } + if c.options.Remote { + args = append(args, "--remote") + } + // If CLIPath is a .js file, run it with node // Note we can't rely on the shebang as Windows doesn't support it command := cliPath diff --git a/go/rpc/generated_rpc.go b/go/rpc/generated_rpc.go index dd5ff61b8..86a1e63c3 100644 --- a/go/rpc/generated_rpc.go +++ b/go/rpc/generated_rpc.go @@ -246,6 +246,7 @@ type RPCTypes struct { UIElicitationStringOneOfField UIElicitationStringOneOfField `json:"UIElicitationStringOneOfField"` UIElicitationStringOneOfFieldOneOf UIElicitationStringOneOfFieldOneOf `json:"UIElicitationStringOneOfFieldOneOf"` UIHandlePendingElicitationRequest UIHandlePendingElicitationRequest `json:"UIHandlePendingElicitationRequest"` + RemoteEnableResult RemoteEnableResult `json:"RemoteEnableResult"` UsageGetMetricsResult UsageGetMetricsResult `json:"UsageGetMetricsResult"` UsageMetricsCodeChanges UsageMetricsCodeChanges `json:"UsageMetricsCodeChanges"` UsageMetricsModelMetric UsageMetricsModelMetric `json:"UsageMetricsModelMetric"` @@ -1947,6 +1948,13 @@ type UsageGetMetricsResult struct { TotalUserRequests int64 `json:"totalUserRequests"` } +type RemoteEnableResult struct { + // Mission Control frontend URL for this session + URL *string `json:"url,omitempty"` + // Whether remote steering is enabled + RemoteSteerable bool `json:"remoteSteerable"` +} + // Aggregated code change metrics type UsageMetricsCodeChanges struct { // Number of distinct files modified @@ -3663,6 +3671,27 @@ func (a *UsageApi) GetMetrics(ctx context.Context) (*UsageGetMetricsResult, erro return &result, nil } +type RemoteApi sessionApi + +func (a *RemoteApi) Enable(ctx context.Context) (*RemoteEnableResult, error) { + req := map[string]any{"sessionId": a.sessionID} + raw, err := a.client.Request("session.remote.enable", req) + if err != nil { + return nil, err + } + var result RemoteEnableResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +func (a *RemoteApi) Disable(ctx context.Context) error { + req := map[string]any{"sessionId": a.sessionID} + _, err := a.client.Request("session.remote.disable", req) + return err +} + // SessionRpc provides typed session-scoped RPC methods. type SessionRpc struct { common sessionApi // Reuse a single struct instead of allocating one for each service on the heap. @@ -3688,6 +3717,7 @@ type SessionRpc struct { Shell *ShellApi History *HistoryApi Usage *UsageApi + Remote *RemoteApi } func (a *SessionRpc) Suspend(ctx context.Context) (*SuspendResult, error) { @@ -3752,6 +3782,7 @@ func NewSessionRpc(client *jsonrpc2.Client, sessionID string) *SessionRpc { r.Shell = (*ShellApi)(&r.common) r.History = (*HistoryApi)(&r.common) r.Usage = (*UsageApi)(&r.common) + r.Remote = (*RemoteApi)(&r.common) return r } diff --git a/go/types.go b/go/types.go index 2c1f6b67e..039744af8 100644 --- a/go/types.go +++ b/go/types.go @@ -90,6 +90,12 @@ type ClientOptions struct { // This option is only used when the SDK spawns the CLI process; it is ignored // when connecting to an external server via CLIUrl. SessionIdleTimeoutSeconds int + // Remote enables remote session support (Mission Control integration). + // When true, sessions in a GitHub repository working directory are + // accessible from GitHub web and mobile. + // This option is only used when the SDK spawns the CLI process; it is ignored + // when connecting to an external server via CLIUrl. + Remote bool } // TelemetryConfig configures OpenTelemetry integration for the Copilot CLI process. diff --git a/nodejs/src/client.ts b/nodejs/src/client.ts index e9c6fb8c8..d85f9e8b4 100644 --- a/nodejs/src/client.ts +++ b/nodejs/src/client.ts @@ -385,6 +385,7 @@ export class CopilotClient { telemetry: options.telemetry, copilotHome: options.copilotHome, sessionIdleTimeoutSeconds: options.sessionIdleTimeoutSeconds ?? 0, + remote: options.remote ?? false, }; } @@ -1486,6 +1487,10 @@ export class CopilotClient { ); } + if (this.options.remote) { + args.push("--remote"); + } + // Suppress debug/trace output that might pollute stdout const envWithoutNodeDebug = { ...this.options.env }; delete envWithoutNodeDebug.NODE_DEBUG; diff --git a/nodejs/src/generated/rpc.ts b/nodejs/src/generated/rpc.ts index 6836324ab..72f13ae46 100644 --- a/nodejs/src/generated/rpc.ts +++ b/nodejs/src/generated/rpc.ts @@ -1477,6 +1477,17 @@ export interface PluginList { plugins: Plugin[]; } +export interface RemoteEnableResult { + /** + * Mission Control frontend URL for this session + */ + url?: string; + /** + * Whether remote steering is enabled + */ + remoteSteerable: boolean; +} + export interface ServerSkill { /** * Unique identifier for the skill @@ -2495,6 +2506,8 @@ export function createServerRpc(connection: MessageConnection) { return { ping: async (params: PingRequest): Promise => connection.sendRequest("ping", params), + connect: async (params: ConnectRequest): Promise => + connection.sendRequest("connect", params), models: { list: async (params?: ModelsListRequest): Promise => connection.sendRequest("models.list", params), @@ -2722,6 +2735,12 @@ export function createSessionRpc(connection: MessageConnection, sessionId: strin getMetrics: async (): Promise => connection.sendRequest("session.usage.getMetrics", { sessionId }), }, + remote: { + enable: async (): Promise => + connection.sendRequest("session.remote.enable", { sessionId }), + disable: async (): Promise => + connection.sendRequest("session.remote.disable", { sessionId }), + }, }; } diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts index 960d398a9..05aab9cae 100644 --- a/nodejs/src/types.ts +++ b/nodejs/src/types.ts @@ -211,6 +211,16 @@ export interface CopilotClientOptions { * `useStdio: true` (stdio is pre-authenticated by transport). */ tcpConnectionToken?: string; + + /** + * Enable remote session support (Mission Control integration). + * When true, sessions in a GitHub repository working directory are + * accessible from GitHub web and mobile. + * This option is only used when the SDK spawns the CLI process; it is ignored + * when connecting to an external server via {@link cliUrl}. + * @default false + */ + remote?: boolean; } /** diff --git a/python/copilot/client.py b/python/copilot/client.py index ffc011771..9ccdfa128 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -176,6 +176,14 @@ class SubprocessConfig: This option is only used when the SDK spawns the CLI process. """ + remote: bool = False + """Enable remote session support (Mission Control integration). + + When ``True``, sessions in a GitHub repository working directory are + accessible from GitHub web and mobile. + This option is only used when the SDK spawns the CLI process. + """ + @dataclass class ExternalServerConfig: @@ -2367,6 +2375,9 @@ async def _start_cli_server(self) -> None: if cfg.session_idle_timeout_seconds is not None and cfg.session_idle_timeout_seconds > 0: args.extend(["--session-idle-timeout", str(cfg.session_idle_timeout_seconds)]) + if cfg.remote: + args.append("--remote") + # If cli_path is a .js file, run it with node # Note that we can't rely on the shebang as Windows doesn't support it if cli_path.endswith(".js"): diff --git a/python/copilot/generated/rpc.py b/python/copilot/generated/rpc.py index fc3eb7bdf..f12d97e21 100644 --- a/python/copilot/generated/rpc.py +++ b/python/copilot/generated/rpc.py @@ -5036,6 +5036,26 @@ def to_dict(self) -> dict: result["totalNanoAiu"] = from_union([from_int, from_none], self.total_nano_aiu) return result +@dataclass +class RemoteEnableResult: + """Result of enabling remote session.""" + remote_steerable: bool + url: str | None = None + + @staticmethod + def from_dict(obj: Any) -> 'RemoteEnableResult': + assert isinstance(obj, dict) + remote_steerable = from_bool(obj.get("remoteSteerable")) + url = from_union([from_str, from_none], obj.get("url")) + return RemoteEnableResult(remote_steerable, url) + + def to_dict(self) -> dict: + result: dict = {} + result["remoteSteerable"] = from_bool(self.remote_steerable) + if self.url is not None: + result["url"] = from_union([from_str, from_none], self.url) + return result + @dataclass class WorkspacesGetWorkspaceResult: workspace: Workspace | None = None @@ -6798,6 +6818,18 @@ async def get_metrics(self, *, timeout: float | None = None) -> UsageGetMetricsR return UsageGetMetricsResult.from_dict(await self._client.request("session.usage.getMetrics", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) +class RemoteApi: + def __init__(self, client: "JsonRpcClient", session_id: str): + self._client = client + self._session_id = session_id + + async def enable(self, *, timeout: float | None = None) -> RemoteEnableResult: + return RemoteEnableResult.from_dict(await self._client.request("session.remote.enable", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + + async def disable(self, *, timeout: float | None = None) -> None: + await self._client.request("session.remote.disable", {"sessionId": self._session_id}, **_timeout_kwargs(timeout)) + + class SessionRpc: """Typed session-scoped RPC methods.""" def __init__(self, client: "JsonRpcClient", session_id: str): @@ -6824,6 +6856,7 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self.shell = ShellApi(client, session_id) self.history = HistoryApi(client, session_id) self.usage = UsageApi(client, session_id) + self.remote = RemoteApi(client, session_id) async def suspend(self, *, timeout: float | None = None) -> None: await self._client.request("session.suspend", {"sessionId": self._session_id}, **_timeout_kwargs(timeout)) From 9847c1e860482235c1f2d960bd42033f358ce15a Mon Sep 17 00:00:00 2001 From: Patrick Nikoletich Date: Sun, 3 May 2026 21:56:20 -0700 Subject: [PATCH 3/3] fix: add docs-validate skip directives and fix API examples option not in published SDK yet, fragments lack standalone context) - Fix C# permission handler: use Kind=Approved pattern - Fix Go permission handler: use correct 2-param signature Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/features/remote-sessions.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/docs/features/remote-sessions.md b/docs/features/remote-sessions.md index f58438acc..b935287ce 100644 --- a/docs/features/remote-sessions.md +++ b/docs/features/remote-sessions.md @@ -17,6 +17,7 @@ Set `remote: true` when creating the client. Every session in a GitHub repo auto #### **TypeScript** + ```typescript import { CopilotClient } from "@github/copilot-sdk"; @@ -35,6 +36,7 @@ session.on("session.info", (event) => { #### **Python** + ```python from copilot import CopilotClient, SubprocessConfig @@ -53,12 +55,13 @@ session.on(on_event) #### **Go** + ```go client, _ := copilot.NewClient(&copilot.ClientOptions{Remote: true}) session, _ := client.CreateSession(ctx, &copilot.SessionConfig{ WorkingDirectory: "/path/to/github-repo", - OnPermissionRequest: func(req copilot.PermissionRequest) copilot.PermissionResponse { - return copilot.PermissionResponse{Allowed: true} + OnPermissionRequest: func(req copilot.PermissionRequest, inv copilot.PermissionInvocation) (copilot.PermissionRequestResult, error) { + return copilot.PermissionRequestResult{Kind: copilot.PermissionRequestResultKindApproved}, nil }, }) @@ -71,12 +74,14 @@ session.On(func(event copilot.SessionEvent) { #### **C#** + ```csharp var client = new CopilotClient(new CopilotClientOptions { Remote = true }); var session = await client.CreateSessionAsync(new SessionConfig { WorkingDirectory = "/path/to/github-repo", - OnPermissionRequest = async (req, ct) => new() { Allowed = true }, + OnPermissionRequest = (req, inv) => + Task.FromResult(new PermissionRequestResult { Kind = PermissionRequestResultKind.Approved }), }); session.On((SessionEvent e) => @@ -98,6 +103,7 @@ Use `session.rpc.remote.enable()` to start remote access mid-session, and `sessi #### **TypeScript** + ```typescript const result = await session.rpc.remote.enable(); console.log("Remote URL:", result.url); @@ -108,6 +114,7 @@ await session.rpc.remote.disable(); #### **Python** + ```python result = await session.rpc.remote.enable() print(f"Remote URL: {result.url}") @@ -118,6 +125,7 @@ await session.rpc.remote.disable() #### **Go** + ```go result, err := session.RPC.Remote.Enable(ctx) fmt.Println("Remote URL:", *result.URL) @@ -128,6 +136,7 @@ err = session.RPC.Remote.Disable(ctx) #### **C#** + ```csharp var result = await session.Rpc.Remote.EnableAsync(); Console.WriteLine($"Remote URL: {result.Url}");