Skip to content
Open
Show file tree
Hide file tree
Changes from 75 commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
5cdedb7
Add Rust SDK
tclem Apr 28, 2026
7a786c7
Polish public API for 0.1.0 release
tclem Apr 28, 2026
d3a309a
Route generated SessionId/RequestId fields through hand-authored newt…
tclem Apr 28, 2026
b6f65a4
Pass ToolInvocation to define_tool closures
tclem Apr 28, 2026
9e5683f
Make ping message argument optional
tclem Apr 28, 2026
457b63a
Build Rust docs with all features in CI
tclem Apr 29, 2026
8b60330
Merge remote-tracking branch 'origin/main' into tclem/rust-sdk-releas…
tclem Apr 29, 2026
14faf4a
Address PR #1164 review feedback
tclem Apr 29, 2026
9f07406
Regenerate Rust types for @github/copilot 1.0.39-0
tclem Apr 29, 2026
6827987
Scope codegen-check workflow changes to Rust only
tclem Apr 29, 2026
5605747
Point rust-publish-release workflow header to RELEASING.md
tclem Apr 29, 2026
6d450cc
Update Rust scenario binaries for new define_tool signature
tclem Apr 29, 2026
56dbf76
Rename Session::send_message -> send and align MessageOptions
tclem Apr 29, 2026
92b25f8
Wrap subscribe() in EventSubscription / LifecycleSubscription newtypes
tclem Apr 29, 2026
894593f
Apply nightly rustfmt to subscription module
tclem Apr 29, 2026
0a4a257
Fix workspaces RPC method names (was singular `workspace.*`)
tclem Apr 29, 2026
7156735
rust: add typed RPC namespace, route helpers through it
tclem Apr 29, 2026
b136d3f
Rename crate to `github-copilot-sdk`
tclem Apr 29, 2026
7068f87
Add typed wrappers for filter/MCP/permission shapes (Bucket A.1, A.3,…
tclem Apr 29, 2026
056ff6e
Document infinite_sessions parity + Client::stop deferral (Bucket A.2…
tclem Apr 29, 2026
33544f9
Aggregate Client::stop errors across active sessions (Bucket B / A.6)
tclem Apr 29, 2026
ead063b
Add Bucket B.1 SessionConfig fields
tclem Apr 29, 2026
c4132c2
Add Bucket B.2 ClientOptions fields (log_level + idle timeout)
tclem Apr 29, 2026
3b30d5a
Add Bucket B.2 on_list_models BYOK callback override
tclem Apr 29, 2026
8c00cc0
Add MessageOptions.request_headers (Phase 4 § 4.5)
tclem Apr 29, 2026
2a8f4e8
Add slash command registration (Phase 4 § 4.1)
tclem Apr 29, 2026
bfa519d
Add ADR 0001: SessionFsProvider trait and plumbing (Phase 4 § 4.2)
tclem Apr 29, 2026
95a2ece
rust: implement SessionFsProvider (Phase 4 § 4.2)
tclem Apr 29, 2026
7d45a83
Add W3C Trace Context propagation (Phase 4 § 4.3)
tclem Apr 29, 2026
0abe6b2
Implement Default on ToolInvocation for test ergonomics
tclem Apr 29, 2026
aefb108
Add TelemetryConfig env-var passthrough on ClientOptions (Phase 4 § 4.4)
tclem Apr 29, 2026
b502b82
Document Rust-only API surface (Phase 4 § 4.7)
tclem Apr 29, 2026
cd8d6bb
Broaden skills discovery wording in copilot-instructions.md
tclem Apr 29, 2026
3109b77
Fix ConnectionState::Errored wire form to match Go ("error" not "erro…
tclem Apr 29, 2026
9062771
Rename ConnectionState::Errored to ConnectionState::Error
tclem Apr 29, 2026
f4aa8d9
Address PR #1164 cross-SDK consistency review
tclem Apr 29, 2026
37ba14f
Type MessageOptions::mode as DeliveryMode enum
tclem Apr 29, 2026
f3a5987
Default permission-flow flags to Some(true)
tclem Apr 29, 2026
cd3436f
Mark remaining public config types non_exhaustive
tclem Apr 29, 2026
078f0f1
Fix InputOptions doc-link to SessionUi::input
tclem Apr 29, 2026
802dc3b
Drop cross-SDK comparisons from Rust source comments
tclem Apr 29, 2026
85483d5
Move SessionFs ADR out of public crate
tclem Apr 29, 2026
c58e2f2
Fix SessionUi::elicitation wire field name
tclem Apr 29, 2026
a7c8215
Add typed on_auto_mode_switch handler for rate-limit recovery
tclem Apr 29, 2026
64541af
Fix Client::list_sessions wire shape — wrap filter under params.filter
tclem Apr 29, 2026
8308c3f
Bump @github/copilot pin to ^1.0.39 + regen Rust types
tclem Apr 29, 2026
2766a79
Fix CI: rename scenarios crate ref + nightly-fmt regen + non_exhausti…
tclem Apr 29, 2026
32b8b18
Use "GitHub Copilot CLI" consistently in user-facing docs
tclem Apr 29, 2026
a1e61d9
Address PR review: add prompts/attachments scenario + README parity s…
tclem Apr 29, 2026
9c055aa
Add get_model and send_telemetry to Rust-only API list
tclem Apr 29, 2026
da486a0
Fix update-copilot-dependency: format Rust generated output
tclem Apr 29, 2026
0f6a2ed
Fix Client::get_status and Client::get_auth_status wire method names
tclem Apr 29, 2026
4a46f18
Refactor JsonRpcClient writer to actor pattern (cancel-safety)
tclem Apr 30, 2026
9a1d9f3
Add WaiterGuard RAII for Session::send_and_wait (cancel-safety)
tclem Apr 30, 2026
c118701
Cooperative event-loop shutdown via Notify (cancel-safety)
tclem Apr 30, 2026
d97877a
Document cancel-safety for public async APIs (RFD-400)
tclem Apr 30, 2026
19c865d
Merge remote-tracking branch 'origin/main' into tclem/rust-sdk-releas…
tclem Apr 30, 2026
a88c3eb
Add scheduled trigger to update-copilot-dependency.yml
tclem Apr 30, 2026
e04357c
Correct get_quota doc: endpoint exists cross-SDK, only wrapper is Rus…
tclem Apr 30, 2026
2d725a5
Add ClientOptions::new() and Tool::new() builder methods
tclem Apr 30, 2026
d72e205
Add per-field builder methods to SessionConfig and ResumeSessionConfig
tclem Apr 30, 2026
e93ce8e
Scrub github-app references from public-repo files
tclem Apr 30, 2026
a870a3d
Round out builder coverage and document the Option<T> escape hatch
tclem Apr 30, 2026
d20cd3e
Revert "Add scheduled trigger to update-copilot-dependency.yml"
tclem Apr 30, 2026
1742053
Add TelemetryConfig builder methods
tclem Apr 30, 2026
96a710c
Merge remote-tracking branch 'origin/main' into tclem/rust-sdk-releas…
tclem Apr 30, 2026
7a5f57e
Switch build.rs from curl to ureq with bounded retries
tclem Apr 30, 2026
393d158
ci: validate embedded-cli bundle build on macOS / Linux / Windows
tclem Apr 30, 2026
6dd07b9
Fix duplicate user_input dispatch (github-app#4249)
tclem Apr 30, 2026
17a7755
Drop Skills section from .github/copilot-instructions.md
tclem May 2, 2026
c8fe373
Render experimental RPC methods as a styled rustdoc admonition
tclem May 2, 2026
b907681
docs(skill): clarify that handler thread-safety is compiler-enforced
tclem May 2, 2026
7202dfd
Refactor session shutdown from Notify to CancellationToken
tclem May 2, 2026
e96aea6
docs(skill): rewrite "Idioms that don't port" with full research backing
tclem May 2, 2026
6ac706d
Merge remote-tracking branch 'origin/main' into tclem/rust-sdk-releas…
tclem May 2, 2026
a60a54b
Add agentId envelope field on session events for sub-agent attribution
tclem May 2, 2026
acc52d6
Remove parity-audit scratch files
tclem May 2, 2026
8a2d62b
docs(skill): tighten SKILL.md and codify trait-with-defaults extensio…
tclem May 2, 2026
59557da
Add continue_pending_work field to ResumeSessionConfig
tclem May 2, 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
4 changes: 2 additions & 2 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@

## Where to add new code or tests 🧭

- SDK code: `nodejs/src`, `python/copilot`, `go`, `dotnet/src`
- Unit tests: `nodejs/test`, `python/*`, `go/*`, `dotnet/test`
- SDK code: `nodejs/src`, `python/copilot`, `go`, `dotnet/src`, `rust/src`
- Unit tests: `nodejs/test`, `python/*`, `go/*`, `dotnet/test`, `rust/tests`
- E2E tests: `*/e2e/` folders that use the shared replay proxy and `test/snapshots/`
- Generated types: update schema in `@github/copilot` then run `cd nodejs && npm run generate:session-types` and commit generated files in `src/generated` or language generated location.
345 changes: 345 additions & 0 deletions .github/skills/rust-coding-skill/SKILL.md

Large diffs are not rendered by default.

184 changes: 184 additions & 0 deletions .github/skills/rust-coding-skill/examples.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
# Rust Coding Skill — Examples

Patterns specific to the Rust SDK in this repo (`rust/`) that aren't obvious
from general Rust knowledge.

## Defining a tool

### Anti-pattern — building the wire payload by hand

```rust
let raw = serde_json::json!({
"name": "get_weather",
"description": "...",
"parameters": { "type": "object", ... },
});
config.tools = Some(vec![serde_json::from_value(raw)?]);
```

### Preferred — implement `ToolHandler`, route via `ToolHandlerRouter`

```rust
use copilot::tool::{Tool, ToolHandler, ToolHandlerRouter, ToolInvocation, ToolResult};
use copilot::Error;

struct GetWeatherTool;

#[async_trait::async_trait]
impl ToolHandler for GetWeatherTool {
fn tool(&self) -> Tool {
Tool {
name: "get_weather".to_string(),
description: "Get the current weather for a city.".to_string(),
// ..Default::default() — leaves namespaced_name, instructions,
// overrides_built_in_tool, skip_permission at their defaults.
..Default::default()
}
}

async fn call(&self, invocation: ToolInvocation) -> Result<ToolResult, Error> {
// ...
Ok(ToolResult::Text("...".into()))
}
}

use copilot::handler::ApproveAllHandler;
use std::sync::Arc;

let router = ToolHandlerRouter::new(
vec![Box::new(GetWeatherTool)],
Arc::new(ApproveAllHandler),
);
```

## Spans for spawned event loops

The session event loop is spawned per session. Always attach a span so events
emitted inside it correlate.

### Anti-pattern — losing parent context

```rust
tokio::spawn(async move {
while let Some(event) = rx.recv().await {
info!("event {:?}", event); // No span — can't filter by session
}
});
```

### Preferred — `error_span!` + `.instrument()`

```rust
use tracing::Instrument;

let span = tracing::error_span!("session_event_loop", session_id = %id);
tokio::spawn(async move {
while let Some(event) = rx.recv().await {
info!(event_type = ?event.kind, "session event");
}
}.instrument(span));
```

## Concurrent permission handlers

`HandlerEvent::PermissionRequest` and `HandlerEvent::ExternalTool` are dispatched
on spawned tasks (see `rust/src/session.rs:973` and `:1022`). Implementations
must be safe for concurrent invocation.
Comment thread
tclem marked this conversation as resolved.

The `SessionHandler` trait declares `Send + Sync + 'static`, so the compiler
enforces this — handlers with non-`Sync` state (e.g. `RefCell`, `Cell`,
`Rc`) won't compile. The examples below make the rejection mechanism explicit.

### Won't compile — non-`Sync` state

```rust
struct MyHandler {
last_request: std::cell::RefCell<Option<String>>, // RefCell: !Sync
}

#[async_trait]
impl SessionHandler for MyHandler {
// ^^^^^^^^^^^^^^ the trait `Sync` is not implemented for `RefCell<...>`
async fn on_event(&self, event: HandlerEvent) -> HandlerResponse { /* ... */ }
}
```

The error surfaces at the `impl` site, not at use site, because the trait's
`Send + Sync` bound makes `RefCell` ineligible for any field of any type that
implements `SessionHandler`.

### Preferred — `parking_lot::Mutex` or atomics

```rust
struct MyHandler {
last_request: parking_lot::Mutex<Option<String>>, // Mutex<T>: Sync if T: Send
}
```

## Adding a field to a public struct

Adding a field to a public, non-exhaustive struct is a breaking change because
existing callers' struct literals stop compiling. Two patterns soften this:

### Pattern 1 — `Default` + `..Default::default()` in docs

```rust
#[derive(Default)]
pub struct Tool {
pub name: String,
pub description: String,
// new field
pub overrides_built_in_tool: bool,
}

// In docs and examples:
let t = Tool {
name: "x".into(),
description: "y".into(),
..Default::default()
};
```

### Pattern 2 — `#[non_exhaustive]` for types callers shouldn't construct

Use sparingly — only for types that are *only* meant to be received from the
SDK, never built by users.

```rust
#[non_exhaustive]
pub struct CreateSessionResult {
pub session_id: SessionId,
// ...
}
```

## Test handler for non-permission scenarios

When a test doesn't exercise the permission flow, use the SDK's built-in
`ApproveAllHandler` instead of writing a custom one:

```rust
use copilot::handler::ApproveAllHandler;
use copilot::types::SessionConfig;
use std::sync::Arc;

let session = client
.create_session(SessionConfig::default().with_handler(Arc::new(ApproveAllHandler)))
.await?;
```

## Regenerating types after a schema bump

```bash
# 1. Update schema (usually arrives with @github/copilot package update)
cd nodejs && npm install @github/copilot@latest && cd ..

# 2. Regenerate Rust types
cd scripts/codegen && npm run generate:rust

# 3. Verify
cd ../../rust && cargo check --all-features
```

If a generated type changes shape, hand-fix any user-facing wrappers in
`rust/src/types.rs` rather than monkey-patching the generated file.
23 changes: 23 additions & 0 deletions .github/workflows/codegen-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ on:
- 'python/copilot/generated/**'
- 'go/generated_*.go'
- 'go/rpc/**'
- 'rust/src/generated/**'
- '.github/workflows/codegen-check.yml'
workflow_dispatch:

Expand All @@ -34,6 +35,24 @@ jobs:
with:
go-version: '1.22'

# Rust generator runs `cargo fmt` on the output, so we need a toolchain with rustfmt.
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: "1.94.0"
components: rustfmt

# Nightly rustfmt for unstable format options (group_imports,
# imports_granularity, reorder_impl_items) — pinned in
# `rust/.rustfmt.nightly.toml`. The Rust generator emits unconsolidated
# imports under stable rustfmt; nightly fmt consolidates them to match
# the canonical committed form.
- name: Install nightly rustfmt
uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly-2026-04-14
components: rustfmt

- name: Install nodejs SDK dependencies
working-directory: ./nodejs
run: npm ci
Expand All @@ -46,6 +65,10 @@ jobs:
working-directory: ./scripts/codegen
run: npm run generate

- name: Apply nightly rustfmt to generated Rust output
working-directory: ./rust
run: cargo +nightly-2026-04-14 fmt --all -- --config-path .rustfmt.nightly.toml

- name: Check for uncommitted changes
run: |
if [ -n "$(git status --porcelain)" ]; then
Expand Down
54 changes: 54 additions & 0 deletions .github/workflows/rust-publish-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: "Rust SDK: Publish Release"

# Publishes the `copilot-sdk` crate to crates.io when a release-plz
# version-bump PR is merged to `main`. See rust/RELEASING.md for the
# full release process and one-time setup (CARGO_REGISTRY_TOKEN, etc).

on:
push:
branches:
- main
paths:
- 'rust/Cargo.toml'
- 'rust/Cargo.lock'
- 'rust/release-plz.toml'
workflow_dispatch:

permissions:
contents: write

concurrency:
group: rust-release-plz-publish
cancel-in-progress: false

jobs:
publish:
name: Publish to crates.io
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./rust
steps:
- uses: actions/checkout@v6.0.2
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: "1.94.0"

- uses: Swatinem/rust-cache@v2
with:
workspaces: "rust"

- name: Run release-plz release
uses: release-plz/action@v0.5
with:
command: release
manifest_path: rust/Cargo.toml
config: rust/release-plz.toml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
56 changes: 56 additions & 0 deletions .github/workflows/rust-release-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: "Rust SDK: Create Release PR"

# release-plz opens a PR that bumps the `copilot-sdk` version in
# `rust/Cargo.toml` and updates `rust/CHANGELOG.md` based on
# conventional-commit history since the last `rust-vX.Y.Z` tag.
#
# Review and merge that PR on the maintainer's schedule. Publishing to
# crates.io happens separately in `rust-publish-release.yml` once the
# version bump lands on `main`.
#
# Runs manually only — we don't want a PR to race with every push.

on:
workflow_dispatch:

permissions:
contents: write
pull-requests: write

concurrency:
group: rust-release-plz-pr
cancel-in-progress: false

jobs:
release-pr:
name: Create Release PR
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./rust
steps:
- uses: actions/checkout@v6.0.2
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: "1.94.0"

- uses: Swatinem/rust-cache@v2
with:
workspaces: "rust"

- name: Run release-plz release-pr
uses: release-plz/action@v0.5
with:
command: release-pr
manifest_path: rust/Cargo.toml
config: rust/release-plz.toml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# CARGO_REGISTRY_TOKEN is not required for release-pr (no publish),
# but release-plz inspects the crate on crates.io to compute the
# next version. Public crate inspection doesn't need auth.
Loading
Loading