Skip to content
Open
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
ff93ad6
Phase 1: codegen falls back to JsonElement instead of object
SteveSandersonMS May 20, 2026
ecf4432
Phase 3: seal hand-written public types
SteveSandersonMS May 20, 2026
7b21592
Phase 4a: simple property/method renames
SteveSandersonMS May 20, 2026
57d0627
Phase 4b: expand SessionFsProvider abbreviations
SteveSandersonMS May 20, 2026
52ac22a
Phase 4c: shape changes (Streaming, Tools, ToolBinaryResultType, clea…
SteveSandersonMS May 20, 2026
e12fcf0
Phase 4d/4e: remove CopilotClient.State, retype LogLevel
SteveSandersonMS May 20, 2026
c636349
Fix mojibake from cp1252/UTF-8 round-trip in earlier phase commits
SteveSandersonMS May 20, 2026
23626b9
Phase 4f: generic OnLifecycle<T> + On<T>, polymorphic lifecycle events
SteveSandersonMS May 20, 2026
734d80d
Phase 4g: DateTimeOffset timestamps + PermissionRequestResult.Feedback
SteveSandersonMS May 20, 2026
174b535
Phase 7: SendAsync(string) / SendAndWaitAsync(string) convenience ove…
SteveSandersonMS May 20, 2026
59ddbf5
Phase 9 (partial): rename Cwd/Remote properties on hand-written types
SteveSandersonMS May 20, 2026
a64803a
Phase 8: README updates for OnLifecycle<T> and lifecycle event types
SteveSandersonMS May 20, 2026
ccd1a08
Phase 5: extract SessionConfigBase; seal config classes
SteveSandersonMS May 20, 2026
94715fe
Phase 6: remove named delegate types, use Func<...>
SteveSandersonMS May 20, 2026
7670b99
Phase 9: introduce RuntimeConnection discriminated config
SteveSandersonMS May 20, 2026
1be458e
README: update Custom Permission Handler section for Phase 6 delegate…
SteveSandersonMS May 20, 2026
07b337a
Re-run C# codegen to pick up Phase 1 JsonElement fallback consistently
SteveSandersonMS May 20, 2026
0508ce8
Fix snapshot capture locally
SteveSandersonMS May 20, 2026
50540ac
Fix MCP config serialization
SteveSandersonMS May 20, 2026
129c281
Revert "Phase 1: codegen falls back to JsonElement instead of object"
SteveSandersonMS May 20, 2026
ec6e28a
Post-revert cleanup for Phase 1 revert
SteveSandersonMS May 20, 2026
074ace7
Revert "Fix MCP config serialization"
SteveSandersonMS May 20, 2026
70f3e36
Fixes to E2E tests to restore main behavior
SteveSandersonMS May 20, 2026
f3708e5
Restore TCP transport for two ClientOptions tests
SteveSandersonMS May 20, 2026
9658005
Test harness: throw if both useStdio and Connection are supplied
SteveSandersonMS May 20, 2026
a46ae79
Revert accidental CapiProxy.cs change
SteveSandersonMS May 20, 2026
466c1a2
Test fix
SteveSandersonMS May 20, 2026
6b24197
Update C# doc snippets for renamed APIs
SteveSandersonMS May 20, 2026
e1b5eff
Update C# test scenarios for renamed APIs
SteveSandersonMS May 20, 2026
f2d90ff
Drop .SDK from C# namespaces
SteveSandersonMS May 20, 2026
8dbab2d
Fix local test runs
SteveSandersonMS May 20, 2026
77dc443
Drop unused uriConn binding in Client.cs is-check
SteveSandersonMS May 20, 2026
a50f6a1
Address Copilot review feedback
SteveSandersonMS May 20, 2026
5bfb4ea
Docs validation: prepend 'using GitHub.Copilot;' after namespace rename
SteveSandersonMS May 20, 2026
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
78 changes: 39 additions & 39 deletions dotnet/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copilot SDK
# Copilot SDK

SDK for programmatic control of GitHub Copilot CLI.

Expand Down Expand Up @@ -43,7 +43,7 @@ await using var session = await client.CreateSessionAsync(new SessionConfig
// Wait for the response using the session.idle event
var done = new TaskCompletionSource();

session.On(evt =>
session.On<SessionEvent>(evt =>
{
if (evt is AssistantMessageEvent msg)
{
Expand Down Expand Up @@ -72,20 +72,24 @@ new CopilotClient(CopilotClientOptions? options = null)

**Options:**

- `CliPath` - Path to CLI executable (default: `COPILOT_CLI_PATH` env var, or bundled CLI)
- `CliArgs` - Extra arguments prepended before SDK-managed flags
- `CliUrl` - URL of existing CLI server to connect to (e.g., `"localhost:8080"`). When provided, the client will not spawn a CLI process.
- `Port` - Server port (default: 0 for random)
- `UseStdio` - Use stdio transport instead of TCP (default: true)
- `LogLevel` - Log level (default: "info")
- `AutoStart` - Auto-start server (default: true)
- `Cwd` - Working directory for the CLI process
- `CopilotHome` - Base directory for Copilot data (session state, config, etc.). Sets `COPILOT_HOME` on the spawned CLI process. When not set, the CLI defaults to `~/.copilot`. Useful in restricted environments where only specific directories are writable. Ignored when using `CliUrl`.
- `Environment` - Environment variables to pass to the CLI process
- `Logger` - `ILogger` instance for SDK logging
- `Connection` - How to connect to the Copilot runtime. Defaults to `null` (equivalent to `RuntimeConnection.Stdio()` 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.
- `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.Uri(...)`.
- `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 `CliUrl`.
- `Telemetry` - OpenTelemetry configuration for the CLI process. Providing this enables telemetry — no separate flag needed. See [Telemetry](#telemetry) below.
- `UseLoggedInUser` - Whether to use logged-in user for authentication (default: true, but false when `GitHubToken` is provided). Cannot be used with `RuntimeConnection.Uri(...)`.
- `Telemetry` - OpenTelemetry configuration for the runtime process. Providing this enables telemetry — no separate flag needed. See [Telemetry](#telemetry) below.

#### RuntimeConnection

`CopilotClientOptions.Connection` describes how the SDK reaches a Copilot runtime. There are three flavors, all constructed via static factories:

- `RuntimeConnection.Stdio(path?, args?)` — spawns the runtime as a child process and communicates over stdio. This is the default when `Connection` is null.
- `RuntimeConnection.Tcp(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.Uri(...)`.
- `RuntimeConnection.Uri(url, connectionToken?)` — connects to an already-running runtime at `url` (e.g., `"localhost:8080"`). Does not spawn a process.

#### Methods

Expand Down Expand Up @@ -153,35 +157,31 @@ Get the ID of the session currently displayed in the TUI. Only available when co

Request the TUI to switch to displaying the specified session. Only available in TUI+server mode.

##### `On(Action<SessionLifecycleEvent> handler): IDisposable`
##### `OnLifecycle<T>(Action<T> handler): IDisposable where T : SessionLifecycleEvent`

Subscribe to all session lifecycle events. Returns an `IDisposable` that unsubscribes when disposed.
Subscribe to session lifecycle events. Pass a derived type to filter by kind, or `SessionLifecycleEvent` to receive every lifecycle event. Returns an `IDisposable` that unsubscribes when disposed.

```csharp
using var subscription = client.On(evt =>
// Receive every lifecycle event:
using var subscription = client.OnLifecycle<SessionLifecycleEvent>(evt =>
{
Console.WriteLine($"Session {evt.SessionId}: {evt.Type}");
});
```

##### `On(string eventType, Action<SessionLifecycleEvent> handler): IDisposable`

Subscribe to a specific lifecycle event type. Use `SessionLifecycleEventTypes` constants.

```csharp
using var subscription = client.On(SessionLifecycleEventTypes.Foreground, evt =>
// Only receive foreground events:
using var foreground = client.OnLifecycle<SessionForegroundEvent>(evt =>
{
Console.WriteLine($"Session {evt.SessionId} is now in foreground");
});
```

**Lifecycle Event Types:**

- `SessionLifecycleEventTypes.Created` - A new session was created
- `SessionLifecycleEventTypes.Deleted` - A session was deleted
- `SessionLifecycleEventTypes.Updated` - A session was updated
- `SessionLifecycleEventTypes.Foreground` - A session became the foreground session in TUI
- `SessionLifecycleEventTypes.Background` - A session is no longer the foreground session
- `SessionCreatedEvent` — A new session was created
- `SessionDeletedEvent` — A session was deleted
- `SessionUpdatedEvent` — A session was updated
- `SessionForegroundEvent` — A session became the foreground session in TUI
- `SessionBackgroundEvent` — A session is no longer the foreground session

---

Expand All @@ -208,12 +208,12 @@ Send a message to the session.

Returns the message ID.

##### `On(SessionEventHandler handler): IDisposable`
##### `On(Action<SessionEvent> handler): IDisposable`

Subscribe to session events. Returns a disposable to unsubscribe.

```csharp
var subscription = session.On(evt =>
var subscription = session.On<SessionEvent>(evt =>
{
Console.WriteLine($"Event: {evt.Type}");
});
Expand All @@ -226,7 +226,7 @@ subscription.Dispose();

Abort the currently processing message in this session.

##### `GetMessagesAsync(): Task<IReadOnlyList<SessionEvent>>`
##### `GetEventsAsync(): Task<IReadOnlyList<SessionEvent>>`

Get all events/messages from this session.

Expand Down Expand Up @@ -262,7 +262,7 @@ Sessions emit various events during processing. Each event type is a class that
Use pattern matching to handle specific event types:

```csharp
session.On(evt =>
session.On<SessionEvent>(evt =>
{
switch (evt)
{
Expand Down Expand Up @@ -330,7 +330,7 @@ var session = await client.CreateSessionAsync(new SessionConfig
// Use TaskCompletionSource to wait for completion
var done = new TaskCompletionSource();

session.On(evt =>
session.On<SessionEvent>(evt =>
{
switch (evt)
{
Expand Down Expand Up @@ -558,15 +558,15 @@ if (session.Capabilities.Ui?.Elicitation == true)
["production", "staging", "dev"]);

// Text input — returns string or null
string? name = await session.Ui.InputAsync("Project name:", new InputOptions
string? name = await session.Ui.InputAsync("Project name:", new UiInputOptions
{
Title = "Name",
MinLength = 1,
MaxLength = 50,
});

// Generic elicitation with full schema control
ElicitationResult result = await session.Ui.ElicitationAsync(new ElicitationParams
ElicitationResult result = await session.Ui.ElicitAsync(new ElicitationParams
{
Message = "Configure deployment",
RequestedSchema = new ElicitationSchema
Expand Down Expand Up @@ -749,7 +749,7 @@ var session = await client.CreateSessionAsync(new SessionConfig

### Custom Permission Handler

Provide your own `PermissionRequestHandler` delegate to inspect each request and apply custom logic:
Provide your own permission handler (`Func<PermissionRequest, ToolInvocation, Task<PermissionRequestResult>>`) to inspect each request and apply custom logic:
Comment thread
SteveSandersonMS marked this conversation as resolved.
Outdated

```csharp
var session = await client.CreateSessionAsync(new SessionConfig
Expand Down Expand Up @@ -987,7 +987,7 @@ catch (Exception ex)
## Requirements

- .NET 8.0 or later
- GitHub Copilot CLI installed and in PATH (or provide custom `CliPath`)
- GitHub Copilot CLI installed and in PATH (or provide custom `Connection = RuntimeConnection.Stdio(path: ...)`)

## License

Expand Down
2 changes: 1 addition & 1 deletion dotnet/samples/Chat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
OnPermissionRequest = PermissionHandler.ApproveAll
});

using var _ = session.On(evt =>
using var _ = session.On<SessionEvent>(evt =>
{
Console.ForegroundColor = ConsoleColor.Blue;
switch (evt)
Expand Down
2 changes: 1 addition & 1 deletion dotnet/samples/ManualToolResume.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ static async Task<T> WaitForEventAsync<T>(CopilotSession session, Func<T, bool>?
{
var tcs = new TaskCompletionSource<T>(TaskCreationOptions.RunContinuationsAsynchronously);
IDisposable? subscription = null;
subscription = session.On(evt =>
subscription = session.On<SessionEvent>(evt =>
{
if (evt is T typed && (predicate?.Invoke(typed) ?? true))
{
Expand Down
Loading
Loading