Skip to content

Commit ad6f514

Browse files
committed
Merge remote-tracking branch 'upstream/main' into otel-genai-convention-fixes
# Conflicts: # go/session.go # python/copilot/session.py
2 parents 92cca0c + dcd86c1 commit ad6f514

42 files changed

Lines changed: 2377 additions & 10 deletions

Some content is hidden

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

.github/aw/actions-lock.json

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"entries": {
3+
"actions/checkout@v6.0.2": {
4+
"repo": "actions/checkout",
5+
"version": "v6.0.2",
6+
"sha": "de0fac2e4500dabe0009e67214ff5f5447ce83dd"
7+
},
8+
"actions/download-artifact@v8.0.0": {
9+
"repo": "actions/download-artifact",
10+
"version": "v8.0.0",
11+
"sha": "70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3"
12+
},
13+
"actions/github-script@v8": {
14+
"repo": "actions/github-script",
15+
"version": "v8",
16+
"sha": "ed597411d8f924073f98dfc5c65a23a2325f34cd"
17+
},
18+
"actions/upload-artifact@v7.0.0": {
19+
"repo": "actions/upload-artifact",
20+
"version": "v7.0.0",
21+
"sha": "bbbca2ddaa5d8feaa63e36b76fdaad77386f024f"
22+
},
23+
"github/gh-aw/actions/setup@v0.52.1": {
24+
"repo": "github/gh-aw/actions/setup",
25+
"version": "v0.52.1",
26+
"sha": "a86e657586e4ac5f549a790628971ec02f6a4a8f"
27+
}
28+
}
29+
}

.github/workflows/publish.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ on:
2222
permissions:
2323
contents: write
2424
id-token: write # Required for OIDC
25+
actions: write # Required to trigger changelog workflow
2526

2627
concurrency:
2728
group: publish
@@ -213,6 +214,10 @@ jobs:
213214
--target ${{ github.sha }}
214215
env:
215216
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
217+
- name: Trigger changelog generation
218+
run: gh workflow run release-changelog.lock.yml -f tag="v${{ needs.version.outputs.version }}"
219+
env:
220+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
216221
- name: Tag Go SDK submodule
217222
if: github.event.inputs.dist-tag == 'latest' || github.event.inputs.dist-tag == 'prerelease'
218223
run: |

.github/workflows/release-changelog.lock.yml

Lines changed: 1185 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
---
2+
description: Generates release notes from merged PRs/commits. Triggered by the publish workflow or manually via workflow_dispatch.
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
tag:
7+
description: "Release tag to generate changelog for (e.g., v0.1.30)"
8+
required: true
9+
type: string
10+
permissions:
11+
contents: read
12+
actions: read
13+
issues: read
14+
pull-requests: read
15+
tools:
16+
github:
17+
toolsets: [default]
18+
edit:
19+
safe-outputs:
20+
create-pull-request:
21+
title-prefix: "[changelog] "
22+
labels: [automation, changelog]
23+
draft: false
24+
update-release:
25+
max: 1
26+
timeout-minutes: 15
27+
---
28+
29+
# Release Changelog Generator
30+
31+
You are an AI agent that generates well-formatted release notes when a release of the Copilot SDK is published.
32+
33+
- **For stable releases** (tag has no prerelease suffix like `-preview`): update `CHANGELOG.md` via a PR AND update the GitHub Release notes.
34+
- **For prerelease releases** (tag contains `-preview` or similar suffix): update the GitHub Release notes ONLY. Do NOT modify `CHANGELOG.md` or create a PR.
35+
36+
Determine which type of release this is by inspecting the tag or fetching the release metadata.
37+
38+
## Context
39+
40+
- Repository: ${{ github.repository }}
41+
- Release tag: ${{ github.event.inputs.tag }}
42+
43+
Use the GitHub API to fetch the release corresponding to `${{ github.event.inputs.tag }}` to get its name, publish date, prerelease status, and other metadata.
44+
45+
## Your Task
46+
47+
### Step 1: Identify the version range
48+
49+
1. The **new version** is the release tag: `${{ github.event.inputs.tag }}`
50+
2. Fetch the release metadata to determine if this is a **stable** or **prerelease** release.
51+
3. Determine the **previous version** to diff against:
52+
- **For stable releases**: find the previous **stable** release (skip prereleases). Check `CHANGELOG.md` for the most recent version heading (`## [vX.Y.Z](...)`), or fall back to listing releases via the API. This means stable changelogs include ALL changes since the last stable release, even if some were already mentioned in prerelease notes.
53+
- **For prerelease releases**: find the most recent release of **any kind** (stable or prerelease) that precedes this one. This way prerelease notes only cover what's new since the last release.
54+
4. If no previous release exists at all, use the first commit in the repo as the starting point.
55+
56+
### Step 2: Gather changes
57+
58+
1. Use the GitHub tools to list commits between the last documented tag (from Step 1) and the new release tag.
59+
2. Also list merged pull requests in that range. For each PR, note:
60+
- PR number and title
61+
- The PR author
62+
- Which SDK(s) were affected (look for prefixes like `[C#]`, `[Python]`, `[Go]`, `[Node]` in the title, or infer from changed files)
63+
3. Ignore:
64+
- Dependabot/bot PRs that only bump internal dependencies (like `Update @github/copilot to ...`) unless they bring user-facing changes
65+
- Merge commits with no meaningful content
66+
- Preview/prerelease-only changes that were already documented
67+
68+
### Step 3: Categorize and write up
69+
70+
Separate the changes into two groups:
71+
72+
1. **Highlighted features**: Any interesting new feature or significant improvement that deserves its own section with a description and code snippet(s). Read the PR diff and source code to understand the feature well enough to write about it.
73+
2. **Other changes**: Bug fixes, minor improvements, and smaller features that can be summarized in a single bullet each.
74+
75+
Only include changes that are **user-visible in the published SDK packages**. Skip anything that only affects docs, CI, build tooling, GitHub workflows, test infrastructure, or other internal-only concerns.
76+
77+
Additionally, identify **new contributors** — anyone whose first merged PR to this repo falls within this release range. You can determine this by checking whether the author has any earlier merged PRs in the repository.
78+
79+
### Step 4: Update CHANGELOG.md (stable releases only)
80+
81+
**Skip this step entirely for prerelease releases.**
82+
83+
1. Read the current `CHANGELOG.md` file.
84+
2. Add the new version entry **at the top** of the file, right after the title/header.
85+
86+
**Format for each highlighted feature** — use an `### Feature:` or `### Fix:` heading, a 1-2 sentence description explaining what it does and why it matters, and at least one short code snippet (max 3 lines). Focus on **TypeScript** and **C#** as the primary languages. Only show Go/Python when giving a list of one-liner equivalents across all languages, or when their usage pattern is meaningfully different.
87+
88+
**Format for other changes** — a single `### Other changes` section with a flat bulleted list. Each bullet has a lowercase prefix (`feature:`, `bugfix:`, `improvement:`) and a one-line description linking to the PR. **However, if there are no highlighted features above it, omit the `### Other changes` heading entirely** — just list the bullets directly under the version heading.
89+
90+
3. Use the release's publish date (from the GitHub Release metadata), not today's date. For `workflow_dispatch` runs, fetch the release by tag to get the date.
91+
4. If there are new contributors, add a `### New contributors` section at the end listing each with a link to their first PR:
92+
```
93+
### New contributors
94+
- @username made their first contribution in [#123](https://github.com/github/copilot-sdk/pull/123)
95+
```
96+
Omit this section if there are no new contributors.
97+
5. Make sure the existing content below is preserved exactly as-is.
98+
99+
### Step 5: Create a Pull Request (stable releases only)
100+
101+
**Skip this step entirely for prerelease releases.**
102+
103+
Use the `create-pull-request` output to submit your changes. The PR should:
104+
- Have a clear title like "Add changelog for vX.Y.Z"
105+
- Include a brief body summarizing the number of changes
106+
107+
### Step 6: Update the GitHub Release
108+
109+
Use the `update-release` output to replace the auto-generated release notes with your nicely formatted changelog. **Do not include the version heading** (`## [vX.Y.Z](...) (date)`) in the release notes — the release already has a title showing the version. Start directly with the feature sections or other changes list.
110+
111+
## Example Output
112+
113+
Here is an example of what a changelog entry should look like, based on real commits from this repo. **Follow this style exactly.**
114+
115+
````markdown
116+
## [v0.1.28](https://github.com/github/copilot-sdk/releases/tag/v0.1.28) (2026-02-14)
117+
118+
### Feature: support overriding built-in tools
119+
120+
Applications can now override built-in tools such as `edit` or `grep`. To do this, register a custom tool with the same name and set the override flag. ([#636](https://github.com/github/copilot-sdk/pull/636))
121+
122+
```ts
123+
session.defineTool("edit", { isOverride: true }, async (params) => {
124+
// custom edit implementation
125+
});
126+
```
127+
128+
```cs
129+
session.DefineTool("edit", new ToolOptions { IsOverride = true }, async (params) => {
130+
// custom edit implementation
131+
});
132+
```
133+
134+
### Feature: simpler API for changing model mid-session
135+
136+
While `session.rpc.models.setModel()` already worked, there is now a convenience method directly on the session object. ([#621](https://github.com/github/copilot-sdk/pull/621))
137+
138+
- TypeScript: `session.setModel("gpt-4o")`
139+
- C#: `session.SetModel("gpt-4o")`
140+
- Python: `session.set_model("gpt-4o")`
141+
- Go: `session.SetModel("gpt-4o")`
142+
143+
### Other changes
144+
145+
- bugfix: **[Python]** correct `PermissionHandler.approve_all` type annotations ([#618](https://github.com/github/copilot-sdk/pull/618))
146+
- improvement: **[C#]** use event delegate for thread-safe, insertion-ordered event handler dispatch ([#624](https://github.com/github/copilot-sdk/pull/624))
147+
- improvement: **[C#]** deduplicate `OnDisposeCall` and improve implementation ([#626](https://github.com/github/copilot-sdk/pull/626))
148+
- improvement: **[C#]** remove unnecessary `SemaphoreSlim` locks for handler fields ([#625](https://github.com/github/copilot-sdk/pull/625))
149+
150+
### New contributors
151+
152+
- @chlowell made their first contribution in [#586](https://github.com/github/copilot-sdk/pull/586)
153+
- @feici02 made their first contribution in [#566](https://github.com/github/copilot-sdk/pull/566)
154+
````
155+
156+
**Key rules visible in the example:**
157+
- Highlighted features get their own `### Feature:` heading, a short description, and code snippets
158+
- Code snippets are TypeScript and C# primarily; Go/Python only when listing one-liner equivalents or when meaningfully different
159+
- The `### Other changes` section is a flat bulleted list with lowercase `bugfix:` / `feature:` / `improvement:` prefixes
160+
- PR numbers are linked inline, not at the end with author attribution (keep it clean)
161+
162+
## Guidelines
163+
164+
1. **Be concise**: Each bullet should be one short sentence. Don't over-explain.
165+
2. **Be accurate**: Only include changes that actually landed in this release range. Don't hallucinate PRs.
166+
3. **Attribute correctly**: Always link to the PR number. Do not add explicit author attribution.
167+
4. **Skip noise**: Don't include trivial changes (typo fixes in comments, whitespace changes) unless they're the only changes.
168+
5. **Preserve history**: Never modify existing entries in CHANGELOG.md — only prepend new ones.
169+
6. **Handle edge cases**: If there are no meaningful changes (e.g., only internal dependency bumps), still create an entry noting "Internal dependency updates only" or similar.

CHANGELOG.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Changelog
2+
3+
All notable changes to the Copilot SDK are documented in this file.
4+
5+
This changelog is automatically generated by an AI agent when stable releases are published.
6+
See [GitHub Releases](https://github.com/github/copilot-sdk/releases) for the full list.
7+
8+
## [v0.1.30](https://github.com/github/copilot-sdk/releases/tag/v0.1.30) (2026-03-03)
9+
10+
### Feature: support overriding built-in tools
11+
12+
Applications can now override built-in tools such as `grep`, `edit_file`, or `read_file`. To do this, register a custom tool with the same name and set the override flag. Without the flag, the runtime will return an error if the name clashes with a built-in. ([#636](https://github.com/github/copilot-sdk/pull/636))
13+
14+
```ts
15+
import { defineTool } from "@github/copilot-sdk";
16+
17+
const session = await client.createSession({
18+
tools: [defineTool("grep", {
19+
overridesBuiltInTool: true,
20+
handler: async (params) => `CUSTOM_GREP_RESULT: ${params.query}`,
21+
})],
22+
onPermissionRequest: approveAll,
23+
});
24+
```
25+
26+
```cs
27+
var grep = AIFunctionFactory.Create(
28+
([Description("Search query")] string query) => $"CUSTOM_GREP_RESULT: {query}",
29+
"grep",
30+
"Custom grep implementation",
31+
new AIFunctionFactoryOptions
32+
{
33+
AdditionalProperties = new ReadOnlyDictionary<string, object?>(
34+
new Dictionary<string, object?> { ["is_override"] = true })
35+
});
36+
```
37+
38+
### Feature: simpler API for changing model mid-session
39+
40+
While `session.rpc.model.switchTo()` already worked, there is now a convenience method directly on the session object. ([#621](https://github.com/github/copilot-sdk/pull/621))
41+
42+
- TypeScript: `await session.setModel("gpt-4.1")`
43+
- C#: `await session.SetModelAsync("gpt-4.1")`
44+
- Python: `await session.set_model("gpt-4.1")`
45+
- Go: `err := session.SetModel(ctx, "gpt-4.1")`
46+
47+
### Other changes
48+
49+
- improvement: **[C#]** use event delegate for thread-safe, insertion-ordered event handler dispatch ([#624](https://github.com/github/copilot-sdk/pull/624))
50+
- improvement: **[C#]** deduplicate `OnDisposeCall` and improve implementation ([#626](https://github.com/github/copilot-sdk/pull/626))
51+
- improvement: **[C#]** remove unnecessary `SemaphoreSlim` locks for handler fields ([#625](https://github.com/github/copilot-sdk/pull/625))
52+
- bugfix: **[Python]** correct `PermissionHandler.approve_all` type annotations ([#618](https://github.com/github/copilot-sdk/pull/618))
53+
54+
### New contributors
55+
56+
- @giulio-leone made their first contribution in [#618](https://github.com/github/copilot-sdk/pull/618)

dotnet/README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,30 @@ var session = await client.CreateSessionAsync(new SessionConfig
418418

419419
When Copilot invokes `lookup_issue`, the client automatically runs your handler and responds to the CLI. Handlers can return any JSON-serializable value (automatically wrapped), or a `ToolResultAIContent` wrapping a `ToolResultObject` for full control over result metadata.
420420

421+
#### Overriding Built-in Tools
422+
423+
If you register a tool with the same name as a built-in CLI tool (e.g. `edit_file`, `read_file`), the runtime will return an error unless you explicitly opt in by setting `is_override` in the tool's `AdditionalProperties`. This flag signals that you intend to replace the built-in tool with your custom implementation.
424+
425+
```csharp
426+
var editFile = AIFunctionFactory.Create(
427+
async ([Description("File path")] string path, [Description("New content")] string content) => {
428+
// your logic
429+
},
430+
"edit_file",
431+
"Custom file editor with project-specific validation",
432+
new AIFunctionFactoryOptions
433+
{
434+
AdditionalProperties = new ReadOnlyDictionary<string, object?>(
435+
new Dictionary<string, object?> { ["is_override"] = true })
436+
});
437+
438+
var session = await client.CreateSessionAsync(new SessionConfig
439+
{
440+
Model = "gpt-5",
441+
Tools = [editFile],
442+
});
443+
```
444+
421445
### System Message Customization
422446

423447
Control the system prompt using `SystemMessage` in session config:

dotnet/src/Client.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1451,10 +1451,15 @@ internal record CreateSessionRequest(
14511451
internal record ToolDefinition(
14521452
string Name,
14531453
string? Description,
1454-
JsonElement Parameters /* JSON schema */)
1454+
JsonElement Parameters, /* JSON schema */
1455+
bool? OverridesBuiltInTool = null)
14551456
{
14561457
public static ToolDefinition FromAIFunction(AIFunction function)
1457-
=> new ToolDefinition(function.Name, function.Description, function.JsonSchema);
1458+
{
1459+
var overrides = function.AdditionalProperties.TryGetValue("is_override", out var val) && val is true;
1460+
return new ToolDefinition(function.Name, function.Description, function.JsonSchema,
1461+
overrides ? true : null);
1462+
}
14581463
}
14591464

14601465
internal record CreateSessionResponse(

dotnet/src/Session.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,22 @@ await InvokeRpcAsync<object>(
556556
"session.abort", [new SessionAbortRequest { SessionId = SessionId }], cancellationToken);
557557
}
558558

559+
/// <summary>
560+
/// Changes the model for this session.
561+
/// The new model takes effect for the next message. Conversation history is preserved.
562+
/// </summary>
563+
/// <param name="model">Model ID to switch to (e.g., "gpt-4.1").</param>
564+
/// <param name="cancellationToken">Optional cancellation token.</param>
565+
/// <example>
566+
/// <code>
567+
/// await session.SetModelAsync("gpt-4.1");
568+
/// </code>
569+
/// </example>
570+
public async Task SetModelAsync(string model, CancellationToken cancellationToken = default)
571+
{
572+
await Rpc.Model.SwitchToAsync(model, cancellationToken);
573+
}
574+
559575
/// <summary>
560576
/// Disposes the <see cref="CopilotSession"/> and releases all associated resources.
561577
/// </summary>

dotnet/src/Types.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -837,6 +837,7 @@ protected SessionConfig(SessionConfig? other)
837837
public string? ConfigDir { get; set; }
838838

839839
public ICollection<AIFunction>? Tools { get; set; }
840+
840841
public SystemMessageConfig? SystemMessage { get; set; }
841842
public List<string>? AvailableTools { get; set; }
842843
public List<string>? ExcludedTools { get; set; }

dotnet/test/SessionTests.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,4 +441,19 @@ public async Task Should_Create_Session_With_Custom_Config_Dir()
441441
Assert.NotNull(assistantMessage);
442442
Assert.Contains("2", assistantMessage!.Data.Content);
443443
}
444+
445+
[Fact]
446+
public async Task Should_Set_Model_On_Existing_Session()
447+
{
448+
var session = await CreateSessionAsync();
449+
450+
// Subscribe for the model change event before calling SetModelAsync
451+
var modelChangedTask = TestHelper.GetNextEventOfTypeAsync<SessionModelChangeEvent>(session);
452+
453+
await session.SetModelAsync("gpt-4.1");
454+
455+
// Verify a model_change event was emitted with the new model
456+
var modelChanged = await modelChangedTask;
457+
Assert.Equal("gpt-4.1", modelChanged.Data.NewModel);
458+
}
444459
}

0 commit comments

Comments
 (0)