Skip to content

Commit 2d65869

Browse files
jmoseleyCopilot
andcommitted
Merge main into PR #790
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2 parents 0081531 + 7463c54 commit 2d65869

78 files changed

Lines changed: 2137 additions & 1892 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,238 @@ All notable changes to the Copilot SDK are documented in this file.
55
This changelog is automatically generated by an AI agent when stable releases are published.
66
See [GitHub Releases](https://github.com/github/copilot-sdk/releases) for the full list.
77

8+
## [v0.2.0](https://github.com/github/copilot-sdk/releases/tag/v0.2.0) (2026-03-20)
9+
10+
This is a big update with a broad round of API refinements, new capabilities, and cross-SDK consistency improvements that have shipped incrementally through preview releases since v0.1.32.
11+
12+
## Highlights
13+
14+
### Fine-grained system prompt customization
15+
16+
A new `"customize"` mode for `systemMessage` lets you surgically edit individual sections of the Copilot system prompt — without replacing the entire thing. Ten sections are configurable: `identity`, `tone`, `tool_efficiency`, `environment_context`, `code_change_rules`, `guidelines`, `safety`, `tool_instructions`, `custom_instructions`, and `last_instructions`.
17+
18+
Each section supports four static actions (`replace`, `remove`, `append`, `prepend`) and a `transform` callback that receives the current rendered content and returns modified text — useful for regex mutations, conditional edits, or logging what the prompt contains. ([#816](https://github.com/github/copilot-sdk/pull/816))
19+
20+
```ts
21+
const session = await client.createSession({
22+
onPermissionRequest: approveAll,
23+
systemMessage: {
24+
mode: "customize",
25+
sections: {
26+
identity: {
27+
action: (current) => current.replace("GitHub Copilot", "Acme Assistant"),
28+
},
29+
tone: { action: "replace", content: "Be concise and professional." },
30+
code_change_rules: { action: "remove" },
31+
},
32+
},
33+
});
34+
```
35+
36+
```cs
37+
var session = await client.CreateSessionAsync(new SessionConfig {
38+
OnPermissionRequest = PermissionHandler.ApproveAll,
39+
SystemMessage = new SystemMessageConfig {
40+
Mode = SystemMessageMode.Customize,
41+
Sections = new Dictionary<string, SectionOverride> {
42+
["identity"] = new() {
43+
Transform = current => Task.FromResult(current.Replace("GitHub Copilot", "Acme Assistant")),
44+
},
45+
["tone"] = new() { Action = SectionOverrideAction.Replace, Content = "Be concise and professional." },
46+
["code_change_rules"] = new() { Action = SectionOverrideAction.Remove },
47+
},
48+
},
49+
});
50+
```
51+
52+
### OpenTelemetry support across all SDKs
53+
54+
All four SDK languages now support distributed tracing with the Copilot CLI. Set `telemetry` in your client options to configure an OTLP exporter; W3C trace context is automatically propagated on `session.create`, `session.resume`, and `session.send`, and restored in tool handlers so tool execution is linked to the originating trace. ([#785](https://github.com/github/copilot-sdk/pull/785))
55+
56+
```ts
57+
const client = new CopilotClient({
58+
telemetry: {
59+
otlpEndpoint: "http://localhost:4318",
60+
sourceName: "my-app",
61+
},
62+
});
63+
```
64+
65+
```cs
66+
var client = new CopilotClient(new CopilotClientOptions {
67+
Telemetry = new TelemetryConfig {
68+
OtlpEndpoint = "http://localhost:4318",
69+
SourceName = "my-app",
70+
},
71+
});
72+
```
73+
74+
- Python: `CopilotClient(SubprocessConfig(telemetry={"otlp_endpoint": "http://localhost:4318", "source_name": "my-app"}))`
75+
- Go: `copilot.NewClient(&copilot.ClientOptions{Telemetry: &copilot.TelemetryConfig{OTLPEndpoint: "http://localhost:4318", SourceName: "my-app"}})`
76+
77+
### Blob attachments for inline binary data
78+
79+
A new `blob` attachment type lets you send images or other binary content directly to a session without writing to disk — useful when data is already in memory (screenshots, API responses, generated images). ([#731](https://github.com/github/copilot-sdk/pull/731))
80+
81+
```ts
82+
await session.send({
83+
prompt: "What's in this image?",
84+
attachments: [{ type: "blob", data: base64Str, mimeType: "image/png" }],
85+
});
86+
```
87+
88+
```cs
89+
await session.SendAsync(new MessageOptions {
90+
Prompt = "What's in this image?",
91+
Attachments = [new UserMessageDataAttachmentsItemBlob { Data = base64Str, MimeType = "image/png" }],
92+
});
93+
```
94+
95+
### Pre-select a custom agent at session creation
96+
97+
You can now specify which custom agent should be active when a session starts, eliminating the need for a separate `session.rpc.agent.select()` call. ([#722](https://github.com/github/copilot-sdk/pull/722))
98+
99+
```ts
100+
const session = await client.createSession({
101+
customAgents: [
102+
{ name: "researcher", prompt: "You are a research assistant." },
103+
{ name: "editor", prompt: "You are a code editor." },
104+
],
105+
agent: "researcher",
106+
onPermissionRequest: approveAll,
107+
});
108+
```
109+
110+
```cs
111+
var session = await client.CreateSessionAsync(new SessionConfig {
112+
CustomAgents = [
113+
new CustomAgentConfig { Name = "researcher", Prompt = "You are a research assistant." },
114+
new CustomAgentConfig { Name = "editor", Prompt = "You are a code editor." },
115+
],
116+
Agent = "researcher",
117+
OnPermissionRequest = PermissionHandler.ApproveAll,
118+
});
119+
```
120+
121+
---
122+
123+
## New features
124+
125+
- **`skipPermission` on tool definitions** — Tools can now be registered with `skipPermission: true` to bypass the confirmation prompt for low-risk operations like read-only queries. Available in all four SDKs. ([#808](https://github.com/github/copilot-sdk/pull/808))
126+
- **`reasoningEffort` when switching models** — All SDKs now accept an optional `reasoningEffort` parameter in `setModel()` for models that support it. ([#712](https://github.com/github/copilot-sdk/pull/712))
127+
- **Custom model listing for BYOK** — Applications using bring-your-own-key providers can supply `onListModels` in client options to override `client.listModels()` with their own model list. ([#730](https://github.com/github/copilot-sdk/pull/730))
128+
- **`no-result` permission outcome** — Permission handlers can now return `"no-result"` so extensions can attach to sessions without actively answering permission requests. ([#802](https://github.com/github/copilot-sdk/pull/802))
129+
- **`SessionConfig.onEvent` catch-all** — A new `onEvent` handler on session config is registered *before* the RPC is issued, guaranteeing that early events like `session.start` are never dropped. ([#664](https://github.com/github/copilot-sdk/pull/664))
130+
- **Node.js CJS compatibility** — The Node.js SDK now ships both ESM and CJS builds, fixing crashes in VS Code extensions and other tools bundled with esbuild's `format: "cjs"`. No changes needed in consumer code. ([#546](https://github.com/github/copilot-sdk/pull/546))
131+
- **Experimental API annotations** — APIs marked experimental in the schema (agent, fleet, compaction groups) are now annotated in all four SDKs: `[Experimental]` in C#, `/** @experimental */` in TypeScript, and comments in Python and Go. ([#875](https://github.com/github/copilot-sdk/pull/875))
132+
- **System notifications and session log APIs** — Updated to match the latest CLI runtime, adding `system.notification` events and a session log RPC API. ([#737](https://github.com/github/copilot-sdk/pull/737))
133+
134+
## Improvements
135+
136+
- **[.NET, Go]** Serialize event dispatch so handlers are invoked in registration order with no concurrent calls ([#791](https://github.com/github/copilot-sdk/pull/791))
137+
- **[Go]** Detach CLI process lifespan from the context passed to `Client.Start` so cancellation no longer kills the child process ([#689](https://github.com/github/copilot-sdk/pull/689))
138+
- **[Go]** Stop RPC client logging expected EOF errors ([#609](https://github.com/github/copilot-sdk/pull/609))
139+
- **[.NET]** Emit XML doc comments from schema descriptions in generated RPC code ([#724](https://github.com/github/copilot-sdk/pull/724))
140+
- **[.NET]** Use lazy property initialization in generated RPC classes ([#725](https://github.com/github/copilot-sdk/pull/725))
141+
- **[.NET]** Add `DebuggerDisplay` attribute to `SessionEvent` for easier debugging ([#726](https://github.com/github/copilot-sdk/pull/726))
142+
- **[.NET]** Optional RPC params are now represented as optional method params for forward-compatible generated code ([#733](https://github.com/github/copilot-sdk/pull/733))
143+
- **[.NET]** Replace `Task.WhenAny` + `Task.Delay` timeout pattern with `.WaitAsync(TimeSpan)` ([#805](https://github.com/github/copilot-sdk/pull/805))
144+
- **[.NET]** Add NuGet package icon ([#688](https://github.com/github/copilot-sdk/pull/688))
145+
- **[Node]** Don't resolve `cliPath` when `cliUrl` is already set ([#787](https://github.com/github/copilot-sdk/pull/787))
146+
147+
## New RPC methods
148+
149+
We've added low-level RPC methods to control a lot more of what's going on in the session. These are emerging APIs that don't yet have friendly wrappers, and some may be flagged as experimental or subject to change.
150+
151+
- `session.rpc.skills.list()`, `.enable(name)`, `.disable(name)`, `.reload()`
152+
- `session.rpc.mcp.list()`, `.enable(name)`, `.disable(name)`, `.reload()`
153+
- `session.rpc.extensions.list()`, `.enable(name)`, `.disable(name)`, `.reload()`
154+
- `session.rpc.plugins.list()`
155+
- `session.rpc.ui.elicitation(...)` — structured user input
156+
- `session.rpc.shell.exec(command)`, `.kill(pid)`
157+
- `session.log(message, level, ephemeral)`
158+
159+
In an forthcoming update, we'll add friendlier wrappers for these.
160+
161+
## Bug fixes
162+
163+
- **[.NET]** Fix `SessionEvent.ToJson()` failing for events with `JsonElement`-backed payloads (`assistant.message`, `tool.execution_start`, etc.) ([#868](https://github.com/github/copilot-sdk/pull/868))
164+
- **[.NET]** Add fallback `TypeInfoResolver` for `StreamJsonRpc.RequestId` to fix NativeAOT compatibility ([#783](https://github.com/github/copilot-sdk/pull/783))
165+
- **[.NET]** Fix codegen for discriminated unions nested within other types ([#736](https://github.com/github/copilot-sdk/pull/736))
166+
- **[.NET]** Handle unknown session event types gracefully instead of throwing ([#881](https://github.com/github/copilot-sdk/pull/881))
167+
168+
---
169+
170+
## ⚠️ Breaking changes
171+
172+
### All SDKs
173+
174+
- **`autoRestart` removed** — The `autoRestart` option has been deprecated across all SDKs (it was never fully implemented). The property still exists but has no effect and will be removed in a future release. Remove any references to `autoRestart` from your client options. ([#803](https://github.com/github/copilot-sdk/pull/803))
175+
176+
### Python
177+
178+
The Python SDK received a significant API surface overhaul in this release, replacing loosely-typed `TypedDict` config objects with proper keyword arguments and dataclasses. These changes improve IDE autocompletion, type safety, and readability.
179+
180+
- **`CopilotClient` constructor redesigned** — The `CopilotClientOptions` TypedDict has been replaced by two typed config dataclasses. ([#793](https://github.com/github/copilot-sdk/pull/793))
181+
182+
```python
183+
# Before (v0.1.x)
184+
client = CopilotClient({"cli_url": "localhost:3000"})
185+
client = CopilotClient({"cli_path": "/usr/bin/copilot", "log_level": "debug"})
186+
187+
# After (v0.2.0)
188+
client = CopilotClient(ExternalServerConfig(url="localhost:3000"))
189+
client = CopilotClient(SubprocessConfig(cli_path="/usr/bin/copilot", log_level="debug"))
190+
```
191+
192+
- **`create_session()` and `resume_session()` now take keyword arguments** instead of a `SessionConfig` / `ResumeSessionConfig` TypedDict. `on_permission_request` is now a required keyword argument. ([#587](https://github.com/github/copilot-sdk/pull/587))
193+
194+
```python
195+
# Before
196+
session = await client.create_session({
197+
"on_permission_request": PermissionHandler.approve_all,
198+
"model": "gpt-4.1",
199+
})
200+
201+
# After
202+
session = await client.create_session(
203+
on_permission_request=PermissionHandler.approve_all,
204+
model="gpt-4.1",
205+
)
206+
```
207+
208+
- **`send()` and `send_and_wait()` take a positional `prompt` string** instead of a `MessageOptions` TypedDict. Attachments and mode are now keyword arguments. ([#814](https://github.com/github/copilot-sdk/pull/814))
209+
210+
```python
211+
# Before
212+
await session.send({"prompt": "Hello!"})
213+
await session.send_and_wait({"prompt": "What is 2+2?"})
214+
215+
# After
216+
await session.send("Hello!")
217+
await session.send_and_wait("What is 2+2?")
218+
```
219+
220+
- **`MessageOptions`, `SessionConfig`, and `ResumeSessionConfig` removed from public API** — These TypedDicts are no longer exported. Use the new keyword-argument signatures directly. ([#587](https://github.com/github/copilot-sdk/pull/587), [#814](https://github.com/github/copilot-sdk/pull/814))
221+
222+
- **Internal modules renamed to private**`copilot.jsonrpc`, `copilot.sdk_protocol_version`, and `copilot.telemetry` are now `copilot._jsonrpc`, `copilot._sdk_protocol_version`, and `copilot._telemetry`. If you were importing from these modules directly, update your imports. ([#884](https://github.com/github/copilot-sdk/pull/884))
223+
224+
- **Typed overloads for `CopilotClient.on()`** — Event registration now uses typed overloads for better autocomplete. This shouldn't break existing code but changes the type signature. ([#589](https://github.com/github/copilot-sdk/pull/589))
225+
226+
### Go
227+
228+
- **`Client.Start()` context no longer kills the CLI process** — Previously, canceling the `context.Context` passed to `Start()` would terminate the spawned CLI process (it used `exec.CommandContext`). Now the CLI process lifespan is independent of that context — call `client.Stop()` or `client.ForceStop()` to shut it down. ([#689](https://github.com/github/copilot-sdk/pull/689))
229+
230+
- **`LogOptions.Ephemeral` changed from `bool` to `*bool`** — This enables proper three-state semantics (unset/true/false). Use `copilot.Bool(true)` instead of a bare `true`. ([#827](https://github.com/github/copilot-sdk/pull/827))
231+
232+
```go
233+
// Before
234+
session.Log(ctx, copilot.LogOptions{Level: copilot.LevelInfo, Ephemeral: true}, "message")
235+
236+
// After
237+
session.Log(ctx, copilot.LogOptions{Level: copilot.LevelInfo, Ephemeral: copilot.Bool(true)}, "message")
238+
```
239+
8240
## [v0.1.32](https://github.com/github/copilot-sdk/releases/tag/v0.1.32) (2026-03-07)
9241

10242
### Feature: backward compatibility with v2 CLI servers

docs/auth/byok.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ const client = new CopilotClient({
335335

336336
```python
337337
from copilot import CopilotClient
338-
from copilot.types import ModelInfo, ModelCapabilities, ModelSupports, ModelLimits
338+
from copilot.client import ModelInfo, ModelCapabilities, ModelSupports, ModelLimits
339339

340340
client = CopilotClient({
341341
"on_list_models": lambda: [

docs/features/custom-agents.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ const session = await client.createSession({
6565

6666
```python
6767
from copilot import CopilotClient
68-
from copilot.types import PermissionRequestResult
68+
from copilot.session import PermissionRequestResult
6969

7070
client = CopilotClient()
7171
await client.start()

docs/features/image-input.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ await session.send({
6969

7070
```python
7171
from copilot import CopilotClient
72-
from copilot.types import PermissionRequestResult
72+
from copilot.session import PermissionRequestResult
7373

7474
client = CopilotClient()
7575
await client.start()

docs/features/skills.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ await session.sendAndWait({ prompt: "Review this code for security issues" });
4343

4444
```python
4545
from copilot import CopilotClient
46-
from copilot.types import PermissionRequestResult
46+
from copilot.session import PermissionRequestResult
4747

4848
async def main():
4949
client = CopilotClient()

docs/features/steering-and-queueing.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ await session.send({
7070

7171
```python
7272
from copilot import CopilotClient
73-
from copilot.types import PermissionRequestResult
73+
from copilot.session import PermissionRequestResult
7474

7575
async def main():
7676
client = CopilotClient()
@@ -229,7 +229,7 @@ await session.send({
229229

230230
```python
231231
from copilot import CopilotClient
232-
from copilot.types import PermissionRequestResult
232+
from copilot.session import PermissionRequestResult
233233

234234
async def main():
235235
client = CopilotClient()

docs/getting-started.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,8 @@ Create `main.py`:
129129

130130
```python
131131
import asyncio
132-
from copilot import CopilotClient, PermissionHandler
132+
from copilot import CopilotClient
133+
from copilot.session import PermissionHandler
133134

134135
async def main():
135136
client = CopilotClient()
@@ -275,7 +276,8 @@ Update `main.py`:
275276
```python
276277
import asyncio
277278
import sys
278-
from copilot import CopilotClient, PermissionHandler
279+
from copilot import CopilotClient
280+
from copilot.session import PermissionHandler
279281
from copilot.generated.session_events import SessionEventType
280282

281283
async def main():
@@ -651,7 +653,8 @@ Update `main.py`:
651653
import asyncio
652654
import random
653655
import sys
654-
from copilot import CopilotClient, PermissionHandler
656+
from copilot import CopilotClient
657+
from copilot.session import PermissionHandler
655658
from copilot.tools import define_tool
656659
from copilot.generated.session_events import SessionEventType
657660
from pydantic import BaseModel, Field
@@ -919,7 +922,8 @@ Create `weather_assistant.py`:
919922
import asyncio
920923
import random
921924
import sys
922-
from copilot import CopilotClient, PermissionHandler
925+
from copilot import CopilotClient
926+
from copilot.session import PermissionHandler
923927
from copilot.tools import define_tool
924928
from copilot.generated.session_events import SessionEventType
925929
from pydantic import BaseModel, Field
@@ -1312,7 +1316,8 @@ const session = await client.createSession({ onPermissionRequest: approveAll });
13121316
<summary><strong>Python</strong></summary>
13131317

13141318
```python
1315-
from copilot import CopilotClient, PermissionHandler
1319+
from copilot import CopilotClient
1320+
from copilot.session import PermissionHandler
13161321

13171322
client = CopilotClient({
13181323
"cli_url": "localhost:4321"

docs/hooks/error-handling.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,18 @@ type ErrorOccurredHandler = (
3535

3636
<!-- docs-validate: hidden -->
3737
```python
38-
from copilot.types import ErrorOccurredHookInput, HookInvocation, ErrorOccurredHookOutput
38+
from copilot.session import ErrorOccurredHookInput, ErrorOccurredHookOutput
3939
from typing import Callable, Awaitable
4040

4141
ErrorOccurredHandler = Callable[
42-
[ErrorOccurredHookInput, HookInvocation],
42+
[ErrorOccurredHookInput, dict[str, str]],
4343
Awaitable[ErrorOccurredHookOutput | None]
4444
]
4545
```
4646
<!-- /docs-validate: hidden -->
4747
```python
4848
ErrorOccurredHandler = Callable[
49-
[ErrorOccurredHookInput, HookInvocation],
49+
[ErrorOccurredHookInput, dict[str, str]],
5050
Awaitable[ErrorOccurredHookOutput | None]
5151
]
5252
```

docs/hooks/post-tool-use.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,18 @@ type PostToolUseHandler = (
3535

3636
<!-- docs-validate: hidden -->
3737
```python
38-
from copilot.types import PostToolUseHookInput, HookInvocation, PostToolUseHookOutput
38+
from copilot.session import PostToolUseHookInput, PostToolUseHookOutput
3939
from typing import Callable, Awaitable
4040

4141
PostToolUseHandler = Callable[
42-
[PostToolUseHookInput, HookInvocation],
42+
[PostToolUseHookInput, dict[str, str]],
4343
Awaitable[PostToolUseHookOutput | None]
4444
]
4545
```
4646
<!-- /docs-validate: hidden -->
4747
```python
4848
PostToolUseHandler = Callable[
49-
[PostToolUseHookInput, HookInvocation],
49+
[PostToolUseHookInput, dict[str, str]],
5050
Awaitable[PostToolUseHookOutput | None]
5151
]
5252
```

0 commit comments

Comments
 (0)