-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Add Rust SDK (technical preview) #1164
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
tclem
wants to merge
79
commits into
main
Choose a base branch
from
tclem/rust-sdk-release-prep
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 75 commits
Commits
Show all changes
79 commits
Select commit
Hold shift + click to select a range
5cdedb7
Add Rust SDK
tclem 7a786c7
Polish public API for 0.1.0 release
tclem d3a309a
Route generated SessionId/RequestId fields through hand-authored newt…
tclem b6f65a4
Pass ToolInvocation to define_tool closures
tclem 9e5683f
Make ping message argument optional
tclem 457b63a
Build Rust docs with all features in CI
tclem 8b60330
Merge remote-tracking branch 'origin/main' into tclem/rust-sdk-releas…
tclem 14faf4a
Address PR #1164 review feedback
tclem 9f07406
Regenerate Rust types for @github/copilot 1.0.39-0
tclem 6827987
Scope codegen-check workflow changes to Rust only
tclem 5605747
Point rust-publish-release workflow header to RELEASING.md
tclem 6d450cc
Update Rust scenario binaries for new define_tool signature
tclem 56dbf76
Rename Session::send_message -> send and align MessageOptions
tclem 92b25f8
Wrap subscribe() in EventSubscription / LifecycleSubscription newtypes
tclem 894593f
Apply nightly rustfmt to subscription module
tclem 0a4a257
Fix workspaces RPC method names (was singular `workspace.*`)
tclem 7156735
rust: add typed RPC namespace, route helpers through it
tclem b136d3f
Rename crate to `github-copilot-sdk`
tclem 7068f87
Add typed wrappers for filter/MCP/permission shapes (Bucket A.1, A.3,…
tclem 056ff6e
Document infinite_sessions parity + Client::stop deferral (Bucket A.2…
tclem 33544f9
Aggregate Client::stop errors across active sessions (Bucket B / A.6)
tclem ead063b
Add Bucket B.1 SessionConfig fields
tclem c4132c2
Add Bucket B.2 ClientOptions fields (log_level + idle timeout)
tclem 3b30d5a
Add Bucket B.2 on_list_models BYOK callback override
tclem 8c00cc0
Add MessageOptions.request_headers (Phase 4 § 4.5)
tclem 2a8f4e8
Add slash command registration (Phase 4 § 4.1)
tclem bfa519d
Add ADR 0001: SessionFsProvider trait and plumbing (Phase 4 § 4.2)
tclem 95a2ece
rust: implement SessionFsProvider (Phase 4 § 4.2)
tclem 7d45a83
Add W3C Trace Context propagation (Phase 4 § 4.3)
tclem 0abe6b2
Implement Default on ToolInvocation for test ergonomics
tclem aefb108
Add TelemetryConfig env-var passthrough on ClientOptions (Phase 4 § 4.4)
tclem b502b82
Document Rust-only API surface (Phase 4 § 4.7)
tclem cd8d6bb
Broaden skills discovery wording in copilot-instructions.md
tclem 3109b77
Fix ConnectionState::Errored wire form to match Go ("error" not "erro…
tclem 9062771
Rename ConnectionState::Errored to ConnectionState::Error
tclem f4aa8d9
Address PR #1164 cross-SDK consistency review
tclem 37ba14f
Type MessageOptions::mode as DeliveryMode enum
tclem f3a5987
Default permission-flow flags to Some(true)
tclem cd3436f
Mark remaining public config types non_exhaustive
tclem 078f0f1
Fix InputOptions doc-link to SessionUi::input
tclem 802dc3b
Drop cross-SDK comparisons from Rust source comments
tclem 85483d5
Move SessionFs ADR out of public crate
tclem c58e2f2
Fix SessionUi::elicitation wire field name
tclem a7c8215
Add typed on_auto_mode_switch handler for rate-limit recovery
tclem 64541af
Fix Client::list_sessions wire shape — wrap filter under params.filter
tclem 8308c3f
Bump @github/copilot pin to ^1.0.39 + regen Rust types
tclem 2766a79
Fix CI: rename scenarios crate ref + nightly-fmt regen + non_exhausti…
tclem 32b8b18
Use "GitHub Copilot CLI" consistently in user-facing docs
tclem a1e61d9
Address PR review: add prompts/attachments scenario + README parity s…
tclem 9c055aa
Add get_model and send_telemetry to Rust-only API list
tclem da486a0
Fix update-copilot-dependency: format Rust generated output
tclem 0f6a2ed
Fix Client::get_status and Client::get_auth_status wire method names
tclem 4a46f18
Refactor JsonRpcClient writer to actor pattern (cancel-safety)
tclem 9a1d9f3
Add WaiterGuard RAII for Session::send_and_wait (cancel-safety)
tclem c118701
Cooperative event-loop shutdown via Notify (cancel-safety)
tclem d97877a
Document cancel-safety for public async APIs (RFD-400)
tclem 19c865d
Merge remote-tracking branch 'origin/main' into tclem/rust-sdk-releas…
tclem a88c3eb
Add scheduled trigger to update-copilot-dependency.yml
tclem e04357c
Correct get_quota doc: endpoint exists cross-SDK, only wrapper is Rus…
tclem 2d725a5
Add ClientOptions::new() and Tool::new() builder methods
tclem d72e205
Add per-field builder methods to SessionConfig and ResumeSessionConfig
tclem e93ce8e
Scrub github-app references from public-repo files
tclem a870a3d
Round out builder coverage and document the Option<T> escape hatch
tclem d20cd3e
Revert "Add scheduled trigger to update-copilot-dependency.yml"
tclem 1742053
Add TelemetryConfig builder methods
tclem 96a710c
Merge remote-tracking branch 'origin/main' into tclem/rust-sdk-releas…
tclem 7a5f57e
Switch build.rs from curl to ureq with bounded retries
tclem 393d158
ci: validate embedded-cli bundle build on macOS / Linux / Windows
tclem 6dd07b9
Fix duplicate user_input dispatch (github-app#4249)
tclem 17a7755
Drop Skills section from .github/copilot-instructions.md
tclem c8fe373
Render experimental RPC methods as a styled rustdoc admonition
tclem b907681
docs(skill): clarify that handler thread-safety is compiler-enforced
tclem 7202dfd
Refactor session shutdown from Notify to CancellationToken
tclem e96aea6
docs(skill): rewrite "Idioms that don't port" with full research backing
tclem 6ac706d
Merge remote-tracking branch 'origin/main' into tclem/rust-sdk-releas…
tclem a60a54b
Add agentId envelope field on session events for sub-agent attribution
tclem acc52d6
Remove parity-audit scratch files
tclem 8a2d62b
docs(skill): tighten SKILL.md and codify trait-with-defaults extensio…
tclem 59557da
Add continue_pending_work field to ResumeSessionConfig
tclem File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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. | ||
|
|
||
| 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. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 }} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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. |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.