Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,121 @@ See [GitHub Releases](https://github.com/github/copilot-sdk/releases) for the fu

Host-owned external tool callbacks are now cancelled when their runtime request completes or their SDK session terminates. The cancellation primitive is idiomatic per SDK: .NET passes a request token to `AIFunction`, Node.js exposes `ToolInvocation.signal`, Go cancels `ToolInvocation.TraceContext`, Java cancels the returned `CompletableFuture`, Python cancels the handler task, and Rust drops the handler future. Go handlers that retain `TraceContext` for background work must derive a separate lifetime because the invocation context is cancelled when the request ends.

### Breaking: Java process settings moved onto out-of-process connections (#2523)

Java `CopilotClientOptions` no longer carries client-wide `cwd` or `environment`. Those process-scoped settings now live directly on `StdioRuntimeConnection` and `TcpRuntimeConnection` as `workingDirectory` and `environment`, because they only apply when the SDK spawns and manages a runtime process. Unlike the equivalent change in .NET, Go, Python, Node.js, and the broader cross-SDK plan, Java had no shared child-process base class to rename, so this is a smaller-scoped move onto the two concrete out-of-process connection types.

Migration:

```java
// Before
var client = new CopilotClient(new CopilotClientOptions()
.setCwd("/srv/app")
.setEnvironment(Map.of("KEY", "value"))
.setConnection(RuntimeConnection.forStdio("/usr/local/bin/copilot")));

// After
var client = new CopilotClient(new CopilotClientOptions()
.setConnection(RuntimeConnection.forStdio("/usr/local/bin/copilot")
.setWorkingDirectory("/srv/app")
.setEnvironment(Map.of("KEY", "value"))));
```

### Breaking: Rust process-scoped options moved onto out-of-process transports (#2523)

`ClientOptions` no longer carries `program`, `prefix_args`, `working_directory`, `env`, `env_remove`, or `extra_args`. These settings never applied to `Transport::InProcess` or `Transport::External` (there's no SDK-managed CLI subprocess to configure in either case), so keeping them on the shared options struct made it possible to set values that were silently ignored. They now live on a new `OutOfProcessOptions` struct carried directly by the transport variants that spawn a CLI process: `Transport::Stdio(OutOfProcessOptions)` and `Transport::Tcp { process: OutOfProcessOptions, .. }`. `Transport::External` intentionally has no such field, since the SDK connects to a server it doesn't own.

This also fixes a long-standing inconsistency: Rust's `env` previously merged into the inherited process environment (`Command::env(k, v)` without clearing first), while every other SDK replaced the environment when the caller supplied one. A non-empty `OutOfProcessOptions::env` now calls `env_clear()` before applying it, matching the other SDKs' replace semantics. SDK-managed variables (auth token, telemetry, `COPILOT_HOME`, keytar-disable, TCP connection token) are still injected before user `env`/`env_remove`, so callers can continue to override or strip them — this ordering is unchanged from before.

Migration:

```rust
// Before
let options = ClientOptions::new()
.with_program(CliProgram::Path("/usr/local/bin/copilot".into()))
.with_cwd("/srv/app")
.with_env([("KEY", "value")])
.with_transport(Transport::Stdio);

// After
let options = ClientOptions::new().with_transport(Transport::Stdio(
OutOfProcessOptions::new()
.with_program(CliProgram::Path("/usr/local/bin/copilot".into()))
.with_working_directory("/srv/app")
.with_env([("KEY", "value")]),
));
```

`Transport::stdio()` is a new convenience constructor for `Transport::Stdio(OutOfProcessOptions::default())`. `OutOfProcessOptions::with_cwd` was renamed `with_working_directory` for clarity and consistency with the field name.

### Breaking: .NET out-of-process launch settings moved (#2523)

`.NET` `CopilotClientOptions` no longer carries `WorkingDirectory` or `Environment`. Those process-scoped settings now live on `StdioRuntimeConnection` and `TcpRuntimeConnection` through the renamed `OutOfProcessRuntimeConnection` base class, because they only apply when the SDK spawns and manages a runtime process. `ChildProcessRuntimeConnection` was renamed to `OutOfProcessRuntimeConnection`.

Migration:

```csharp
// Before
var client = new CopilotClient(new CopilotClientOptions
{
WorkingDirectory = "/srv/app",
Environment = new Dictionary<string, string> { ["KEY"] = "value" },
Connection = RuntimeConnection.ForStdio(path: "/usr/local/bin/copilot"),
});

// After
var client = new CopilotClient(new CopilotClientOptions
{
Connection = RuntimeConnection.ForStdio(path: "/usr/local/bin/copilot")
{
WorkingDirectory = "/srv/app",
Environment = new Dictionary<string, string> { ["KEY"] = "value" },
},
});
```

### Breaking: Python and Node.js out-of-process launch settings moved (#2523)

Python `CopilotClient` and Node.js `CopilotClientOptions` no longer carry client-wide `working_directory` / `workingDirectory` or `env` launch settings. Those process-scoped settings now live on the renamed `OutOfProcessRuntimeConnection` base for stdio/TCP connections, because they only apply when the SDK spawns and manages a runtime process. `ChildProcessRuntimeConnection` was renamed to `OutOfProcessRuntimeConnection` in both packages.

Migration:

```python
# Before
client = CopilotClient(
working_directory="/srv/app",
env={"KEY": "value"},
connection=RuntimeConnection.for_stdio(path="/usr/local/bin/copilot"),
)

# After
client = CopilotClient(
connection=RuntimeConnection.for_stdio(
path="/usr/local/bin/copilot",
working_directory="/srv/app",
env={"KEY": "value"},
),
)
```

```ts
// Before
const client = new CopilotClient({
workingDirectory: "/srv/app",
env: { KEY: "value" },
connection: RuntimeConnection.forStdio({ path: "/usr/local/bin/copilot" }),
});

// After
const client = new CopilotClient({
connection: RuntimeConnection.forStdio({
path: "/usr/local/bin/copilot",
workingDirectory: "/srv/app",
env: { KEY: "value" },
}),
});
```

### Feature: declare application identity with client info

Client options now accept optional client info (application name and version, integration name and version) across all six SDKs, exposed idiomatically per language (`clientInfo` in Node.js, `client_info` in Python and Rust, `ClientInfo` in Go and .NET, `setClientInfo` in Java). When set, the SDK forwards it on the `server.connect` handshake so the telemetry the runtime emits on the connection is attributed to the application and its Copilot integration instead of the runtime's own build. All fields are optional, and leaving client info unset keeps the runtime's default attribution. See [Client info](./docs/features/client-info.md).
Expand Down Expand Up @@ -105,6 +220,30 @@ var session = await client.CreateSessionAsync(new SessionConfig
});
```

### Breaking: Go out-of-process launch settings moved (#2523)

Go `ClientOptions` no longer carries `WorkingDirectory` or `Env`. Those process-scoped settings now live on `StdioConnection` and `TCPConnection`, because they only apply when the SDK spawns and manages an out-of-process runtime. The internal unexported `childProcessConnection` helper was renamed to `outOfProcessConnection`; this does not change the public Go API surface.

Migration:

```go
// Before
client := copilot.NewClient(&copilot.ClientOptions{
Connection: copilot.StdioConnection{Path: "/usr/local/bin/copilot"},
WorkingDirectory: "/srv/app",
Env: []string{"KEY=value"},
})

// After
client := copilot.NewClient(&copilot.ClientOptions{
Connection: copilot.StdioConnection{
Path: "/usr/local/bin/copilot",
WorkingDirectory: "/srv/app",
Env: []string{"KEY=value"},
},
})
```

## [v1.0.7](https://github.com/github/copilot-sdk/releases/tag/v1.0.7) (2026-07-16)

### Feature: in-process (FFI) transport
Expand Down
38 changes: 22 additions & 16 deletions docs/auth/server-to-server-tokens.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,15 @@ const token = process.env.INSTALLATION_TOKEN;
if (!token) throw new Error("INSTALLATION_TOKEN is required");

const client = new CopilotClient({
connection: RuntimeConnection.forStdio(),
env: {
...process.env,
COPILOT_GITHUB_TOKEN: token,
},
connection: RuntimeConnection.forStdio({
env: {
...process.env,
COPILOT_GITHUB_TOKEN: token,
},
}),
useLoggedInUser: false,
});
```

</details>
<details>
<summary><strong>Python</strong></summary>
Expand All @@ -80,8 +80,9 @@ import os
from copilot import CopilotClient, RuntimeConnection

client = CopilotClient(
connection=RuntimeConnection.for_stdio(),
env={**os.environ, "COPILOT_GITHUB_TOKEN": os.environ["INSTALLATION_TOKEN"]},
connection=RuntimeConnection.for_stdio(
env={**os.environ, "COPILOT_GITHUB_TOKEN": os.environ["INSTALLATION_TOKEN"]},
),
use_logged_in_user=False,
)
```
Expand All @@ -106,8 +107,9 @@ func main() {
log.Fatal("INSTALLATION_TOKEN is required")
}
client := copilot.NewClient(&copilot.ClientOptions{
Connection: copilot.StdioConnection{},
Env: append(os.Environ(), "COPILOT_GITHUB_TOKEN="+token),
Connection: copilot.StdioConnection{
Env: append(os.Environ(), "COPILOT_GITHUB_TOKEN="+token),
},
UseLoggedInUser: copilot.Bool(false),
})
_ = client
Expand All @@ -119,13 +121,14 @@ func main() {
<summary><strong>Rust</strong></summary>

```rust
use github_copilot_sdk::{ClientOptions, Transport};
use github_copilot_sdk::{ClientOptions, OutOfProcessOptions, Transport};

fn main() {
let token = std::env::var("INSTALLATION_TOKEN").expect("INSTALLATION_TOKEN is required");
let options = ClientOptions::new()
.with_transport(Transport::Stdio)
.with_env([("COPILOT_GITHUB_TOKEN", token)])
.with_transport(Transport::Stdio(
OutOfProcessOptions::new().with_env([("COPILOT_GITHUB_TOKEN", token)]),
))
.with_use_logged_in_user(false);
drop(options);
}
Expand All @@ -146,10 +149,12 @@ var environment = Environment.GetEnvironmentVariables()
.ToDictionary(entry => (string)entry.Key, entry => entry.Value?.ToString() ?? "");
environment["COPILOT_GITHUB_TOKEN"] = token;

var connection = RuntimeConnection.ForStdio();
connection.Environment = environment;

await using var client = new CopilotClient(new CopilotClientOptions
{
Connection = RuntimeConnection.ForStdio(),
Environment = environment,
Connection = connection,
UseLoggedInUser = false,
});
```
Expand All @@ -161,6 +166,7 @@ await using var client = new CopilotClient(new CopilotClientOptions
```java
import com.github.copilot.CopilotClient;
import com.github.copilot.rpc.CopilotClientOptions;
import com.github.copilot.rpc.RuntimeConnection;
import java.util.HashMap;
import java.util.Objects;

Expand All @@ -170,7 +176,7 @@ var token = Objects.requireNonNull(
environment.put("COPILOT_GITHUB_TOKEN", token);

try (var client = new CopilotClient(new CopilotClientOptions()
.setEnvironment(environment)
.setConnection(RuntimeConnection.forStdio().setEnvironment(environment))
.setUseLoggedInUser(false))) {
// Use the client.
}
Expand Down
22 changes: 20 additions & 2 deletions dotnet/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,8 @@ new CopilotClient(CopilotClientOptions? options = null)

- `Connection` - How to connect to the Copilot runtime. Defaults to `null` (equivalent to `RuntimeConnection.ForStdio()` with the bundled runtime). See "RuntimeConnection" below.
- `LogLevel` - Runtime log level. Accepts well-known values `CopilotLogLevel.None`, `Error`, `Warning`, `Info`, `Debug`, `All`. Defaults to null (the runtime's own default).
- `WorkingDirectory` - Working directory for the runtime process. When not set, the spawned runtime inherits the calling application's current working directory.
- `BaseDirectory` - Base directory for Copilot data (session state, config, etc.). Sets `COPILOT_HOME` on the spawned runtime process. When not set, the runtime defaults to `~/.copilot`. Useful in restricted environments where only specific directories are writable. Ignored when connecting via `RuntimeConnection.ForUri(...)`.
- `EnableRemoteSessions` - Enables remote-session features.
- `Environment` - Environment variables to pass to the runtime process.
- `Logger` - `ILogger` instance for SDK logging.
- `GitHubToken` - GitHub token for authentication. When provided, takes priority over other auth methods.
- `UseLoggedInUser` - Whether to use logged-in user for authentication (default: true, but false when `GitHubToken` is provided). Cannot be used with `RuntimeConnection.ForUri(...)`.
Expand All @@ -108,6 +106,26 @@ new CopilotClient(CopilotClientOptions? options = null)
- `RuntimeConnection.ForTcp(port = 0, connectionToken?, path?, args?)` — spawns the runtime as a child process listening on a TCP port. `port = 0` auto-allocates; if a non-zero port is already in use, startup fails (no fallback). Use `CopilotClient.RuntimePort` after `StartAsync` to read the assigned port. `connectionToken` is required if other clients will connect via `RuntimeConnection.ForUri(...)`.
- `RuntimeConnection.ForUri(url, connectionToken?)` — connects to an already-running runtime at `url` (e.g., `"localhost:8080"`). Does not spawn a process.

`StdioRuntimeConnection` and `TcpRuntimeConnection` both inherit from `OutOfProcessRuntimeConnection`, which carries process-scoped launch settings for SDK-managed runtime processes:

- `Path` - Path to the runtime executable. When not set, the bundled runtime is used.
- `Args` - Extra command-line arguments to pass to the runtime process.
- `WorkingDirectory` - Working directory for the runtime process. When not set, the spawned runtime inherits the calling application's current working directory.
- `Environment` - Environment variables to pass to the runtime process, replacing the inherited environment.

These settings live on the out-of-process connection instead of `CopilotClientOptions` because they do not apply to `RuntimeConnection.ForInProcess()` and do not affect `RuntimeConnection.ForUri(...)`.

```csharp
var client = new CopilotClient(new CopilotClientOptions
{
Connection = RuntimeConnection.ForStdio(path: "/usr/local/bin/copilot")
{
WorkingDirectory = "/srv/app",
Environment = new Dictionary<string, string> { ["KEY"] = "value" },
},
});
```

Managed stdio and TCP connections use the bundled `copilot-runtime[.exe]` and
adjacent `runtime.node` by default. An explicit connection path or
`COPILOT_CLI_PATH` overrides the bundled runtime.
Expand Down
Loading
Loading