From 18ccafae0c86785858cbd87f5f250622d6850a0a Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 18 Jun 2026 09:47:31 -0400 Subject: [PATCH 1/3] Add experimental-API enforcement tooling for TS, Python, Go, and Rust Bring TypeScript, Python, Go, and Rust in line with the C# ([Experimental]) and Java (@CopilotExperimental) SDKs, where consuming an experimental API produces a real diagnostic the user must explicitly suppress. Previously these four languages only had doc comments. - TypeScript: new @github/eslint-plugin-copilot-sdk package with a type-aware no-experimental-api rule keyed off the @experimental JSDoc tag. - Python: @experimental decorator emitting ExperimentalWarning with a configurable warn/error/ignore policy (COPILOT_EXPERIMENTAL); emitted by codegen onto generated experimental methods. - Go: go/analysis analyzer (go/copilotexperimental) that flags references to Experimental:-documented symbols; suppress with //nolint:copilotexperimental. - Rust: experimental Cargo feature; codegen gates experimental RPC methods via conditional visibility (pub with the feature, pub(crate) fallback without) so external callers get a hard compile error while internal SDK code keeps working. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/rust-sdk-tests.yml | 11 +- go/README.md | 11 + go/copilotexperimental/README.md | 33 + .../cmd/copilotexperimental/main.go | 12 + go/copilotexperimental/experimental.go | 197 + go/copilotexperimental/experimental_test.go | 13 + go/copilotexperimental/go.mod | 10 + go/copilotexperimental/go.sum | 8 + .../testdata/src/consumer/consumer.go | 24 + .../testdata/src/sdk/sdk.go | 38 + nodejs/README.md | 19 + nodejs/eslint-plugin/.gitignore | 1 + nodejs/eslint-plugin/README.md | 51 + nodejs/eslint-plugin/eslint.config.js | 21 + nodejs/eslint-plugin/index.js | 33 + nodejs/eslint-plugin/package-lock.json | 1382 +++++++ nodejs/eslint-plugin/package.json | 50 + .../rules/no-experimental-api.js | 96 + .../test/fixtures/consumer-aliased.ts | 5 + .../test/fixtures/consumer-experimental.ts | 13 + .../test/fixtures/consumer-stable.ts | 6 + .../test/fixtures/consumer-suppressed.ts | 6 + .../eslint-plugin/test/fixtures/re-export.ts | 1 + nodejs/eslint-plugin/test/fixtures/sdk.ts | 34 + .../eslint-plugin/test/fixtures/tsconfig.json | 11 + .../test/no-experimental-api.test.js | 53 + python/copilot/__init__.py | 12 + python/copilot/experimental.py | 189 + python/copilot/generated/rpc.py | 200 + python/test_experimental.py | 127 + rust/Cargo.toml | 11 + rust/README.md | 27 + rust/src/generated/rpc.rs | 3388 ++++++++++++++++- scripts/codegen/python.ts | 11 + scripts/codegen/rust.ts | 60 +- 35 files changed, 6109 insertions(+), 55 deletions(-) create mode 100644 go/copilotexperimental/README.md create mode 100644 go/copilotexperimental/cmd/copilotexperimental/main.go create mode 100644 go/copilotexperimental/experimental.go create mode 100644 go/copilotexperimental/experimental_test.go create mode 100644 go/copilotexperimental/go.mod create mode 100644 go/copilotexperimental/go.sum create mode 100644 go/copilotexperimental/testdata/src/consumer/consumer.go create mode 100644 go/copilotexperimental/testdata/src/sdk/sdk.go create mode 100644 nodejs/eslint-plugin/.gitignore create mode 100644 nodejs/eslint-plugin/README.md create mode 100644 nodejs/eslint-plugin/eslint.config.js create mode 100644 nodejs/eslint-plugin/index.js create mode 100644 nodejs/eslint-plugin/package-lock.json create mode 100644 nodejs/eslint-plugin/package.json create mode 100644 nodejs/eslint-plugin/rules/no-experimental-api.js create mode 100644 nodejs/eslint-plugin/test/fixtures/consumer-aliased.ts create mode 100644 nodejs/eslint-plugin/test/fixtures/consumer-experimental.ts create mode 100644 nodejs/eslint-plugin/test/fixtures/consumer-stable.ts create mode 100644 nodejs/eslint-plugin/test/fixtures/consumer-suppressed.ts create mode 100644 nodejs/eslint-plugin/test/fixtures/re-export.ts create mode 100644 nodejs/eslint-plugin/test/fixtures/sdk.ts create mode 100644 nodejs/eslint-plugin/test/fixtures/tsconfig.json create mode 100644 nodejs/eslint-plugin/test/no-experimental-api.test.js create mode 100644 python/copilot/experimental.py create mode 100644 python/test_experimental.py diff --git a/.github/workflows/rust-sdk-tests.yml b/.github/workflows/rust-sdk-tests.yml index f75a5d6a2..6b01148b0 100644 --- a/.github/workflows/rust-sdk-tests.yml +++ b/.github/workflows/rust-sdk-tests.yml @@ -98,7 +98,10 @@ jobs: if: runner.os == 'Linux' env: BUNDLED_CLI_CACHE_DIR: ${{ github.workspace }}/rust/.bundled-cli-cache - run: cargo clippy --all-targets --features test-support -- --no-deps -D warnings -D clippy::unwrap_used -D clippy::disallowed_macros -D clippy::await_holding_invalid_type + # The `experimental` feature is enabled so examples and lints cover the + # gated experimental API surface (experimental methods are `pub` only + # under this feature — see the `experimental` feature in Cargo.toml). + run: cargo clippy --all-targets --features test-support,experimental -- --no-deps -D warnings -D clippy::unwrap_used -D clippy::disallowed_macros -D clippy::await_holding_invalid_type - name: cargo doc if: runner.os == 'Linux' @@ -127,7 +130,11 @@ jobs: # embed it. Tests exec against the setup-copilot CLI via # COPILOT_CLI_PATH (the env override wins over the dev cache). # The dedicated `bundle` job below exercises the embed pipeline. - run: cargo test --no-default-features --features test-support -- --test-threads=4 --nocapture + # `experimental` is enabled because the SDK's own integration tests + # exercise experimental RPC methods directly; the feature gate that + # hides them from external consumers is the Rust analog of C# + # `[Experimental]` / Java `@CopilotExperimental`. + run: cargo test --no-default-features --features test-support,experimental -- --test-threads=4 --nocapture # Validates the bundled-CLI build path on all three supported # platforms. While the regular `cargo test` job above also exercises diff --git a/go/README.md b/go/README.md index b89a76318..bd6674089 100644 --- a/go/README.md +++ b/go/README.md @@ -15,6 +15,17 @@ To use the SDK, you'll need: go get github.com/github/copilot-sdk/go ``` +## Detecting experimental API usage + +The Go SDK ships a companion analyzer, `copilotexperimental`, that reports +references to experimental Copilot SDK APIs in consumer code. Install the tool +and point `go vet` at it: + +```bash +go install github.com/github/copilot-sdk/go/copilotexperimental/cmd/copilotexperimental@latest +go vet -vettool=$(which copilotexperimental) ./... +``` + ## Run the Sample Try the interactive chat sample (from the repo root): diff --git a/go/copilotexperimental/README.md b/go/copilotexperimental/README.md new file mode 100644 index 000000000..e531f1a27 --- /dev/null +++ b/go/copilotexperimental/README.md @@ -0,0 +1,33 @@ +# copilotexperimental + +`copilotexperimental` is a `go vet`-compatible analyzer that reports references +to experimental Copilot SDK APIs in consumer code. + +It detects exported symbols whose doc comments contain an `Experimental:` +marker, including functions, types, methods, and struct fields. + +## Install + +```bash +go install github.com/github/copilot-sdk/go/copilotexperimental/cmd/copilotexperimental@latest +``` + +## Run + +```bash +go vet -vettool=$(which copilotexperimental) ./... +``` + +## Suppress one diagnostic + +Add `//nolint:copilotexperimental` to the same line as the reference: + +```go +_ = sdk.StartCanvas() //nolint:copilotexperimental +``` + +## golangci-lint + +The analyzer can also run through golangci-lint's custom module plugin support. +Use the analyzer name `copilotexperimental`; the same +`//nolint:copilotexperimental` suppression directive applies there as well. diff --git a/go/copilotexperimental/cmd/copilotexperimental/main.go b/go/copilotexperimental/cmd/copilotexperimental/main.go new file mode 100644 index 000000000..75742c6de --- /dev/null +++ b/go/copilotexperimental/cmd/copilotexperimental/main.go @@ -0,0 +1,12 @@ +// Command copilotexperimental runs the copilotexperimental analyzer. +package main + +import ( + "golang.org/x/tools/go/analysis/singlechecker" + + "github.com/github/copilot-sdk/go/copilotexperimental" +) + +func main() { + singlechecker.Main(copilotexperimental.Analyzer) +} diff --git a/go/copilotexperimental/experimental.go b/go/copilotexperimental/experimental.go new file mode 100644 index 000000000..6b56b9ce4 --- /dev/null +++ b/go/copilotexperimental/experimental.go @@ -0,0 +1,197 @@ +// Package copilotexperimental provides a go/analysis analyzer that reports +// references to experimental Copilot SDK APIs. +package copilotexperimental + +import ( + "go/ast" + "go/token" + "strings" + + "golang.org/x/tools/go/analysis" +) + +const ( + analyzerName = "copilotexperimental" + experimentalMarker = "Experimental:" + suppressionDirective = "nolint:copilotexperimental" +) + +// Doc describes the analyzer. +const Doc = `report references to experimental Copilot SDK APIs + +The analyzer marks declarations whose doc comments contain an "Experimental:" +marker and reports downstream references to those objects. + +Suppress an individual diagnostic by adding //nolint:copilotexperimental to the +same line as the reference.` + +type experimentalFact struct{} + +func (*experimentalFact) AFact() {} + +func (*experimentalFact) String() string { return "experimental" } + +// Analyzer reports cross-package references to experimental Copilot SDK APIs. +var Analyzer = &analysis.Analyzer{ + Name: analyzerName, + Doc: Doc, + Run: run, + FactTypes: []analysis.Fact{(*experimentalFact)(nil)}, +} + +func run(pass *analysis.Pass) (any, error) { + exportFacts(pass) + reportUses(pass) + return nil, nil +} + +func exportFacts(pass *analysis.Pass) { + mark := func(id *ast.Ident) { + if id == nil { + return + } + if obj := pass.TypesInfo.Defs[id]; obj != nil { + pass.ExportObjectFact(obj, &experimentalFact{}) + } + } + + for _, file := range pass.Files { + for _, decl := range file.Decls { + switch decl := decl.(type) { + case *ast.FuncDecl: + if hasExperimentalMarker(decl.Doc) { + mark(decl.Name) + } + case *ast.GenDecl: + groupExperimental := len(decl.Specs) == 1 && hasExperimentalMarker(decl.Doc) + for _, spec := range decl.Specs { + switch spec := spec.(type) { + case *ast.TypeSpec: + if groupExperimental || hasExperimentalMarker(spec.Doc) { + mark(spec.Name) + } + markStructFields(pass, spec) + case *ast.ValueSpec: + if groupExperimental || hasExperimentalMarker(spec.Doc) { + for _, name := range spec.Names { + mark(name) + } + } + } + } + } + } + } +} + +func markStructFields(pass *analysis.Pass, spec *ast.TypeSpec) { + structType, ok := spec.Type.(*ast.StructType) + if !ok || structType.Fields == nil { + return + } + + for _, field := range structType.Fields.List { + if !hasExperimentalMarker(field.Doc, field.Comment) { + continue + } + for _, name := range field.Names { + if obj := pass.TypesInfo.Defs[name]; obj != nil { + pass.ExportObjectFact(obj, &experimentalFact{}) + } + } + } +} + +func reportUses(pass *analysis.Pass) { + for _, file := range pass.Files { + suppressions := collectSuppressions(pass, file) + + ast.Inspect(file, func(node ast.Node) bool { + id, ok := node.(*ast.Ident) + if !ok || suppressions.contains(pass, id.Pos()) { + return true + } + + obj := pass.TypesInfo.Uses[id] + if obj == nil || obj.Pkg() == nil || obj.Pkg() == pass.Pkg { + return true + } + + var fact experimentalFact + if !pass.ImportObjectFact(obj, &fact) { + return true + } + + pass.Reportf( + id.Pos(), + "use of experimental API '%s' — opt in with //%s", + obj.Name(), + suppressionDirective, + ) + return true + }) + } +} + +func hasExperimentalMarker(groups ...*ast.CommentGroup) bool { + for _, group := range groups { + if group == nil { + continue + } + for _, line := range strings.Split(group.Text(), "\n") { + if strings.HasPrefix(strings.TrimSpace(line), experimentalMarker) { + return true + } + } + } + return false +} + +type suppressionIndex map[int]struct{} + +func collectSuppressions(pass *analysis.Pass, file *ast.File) suppressionIndex { + lines := make(suppressionIndex) + for _, group := range file.Comments { + for _, comment := range group.List { + if hasSuppressionDirective(comment.Text) { + line := pass.Fset.PositionFor(comment.Slash, false).Line + lines[line] = struct{}{} + } + } + } + return lines +} + +func (index suppressionIndex) contains(pass *analysis.Pass, pos token.Pos) bool { + line := pass.Fset.PositionFor(pos, false).Line + _, ok := index[line] + return ok +} + +func hasSuppressionDirective(text string) bool { + text = normalizeCommentText(text) + if !strings.HasPrefix(text, "nolint:") { + return false + } + + directives := strings.TrimSpace(strings.TrimPrefix(text, "nolint:")) + if directives == "" { + return false + } + + field := strings.Fields(directives)[0] + for _, directive := range strings.Split(field, ",") { + if strings.TrimSpace(directive) == analyzerName { + return true + } + } + return false +} + +func normalizeCommentText(text string) string { + text = strings.TrimSpace(text) + text = strings.TrimPrefix(text, "//") + text = strings.TrimPrefix(text, "/*") + text = strings.TrimSuffix(text, "*/") + return strings.TrimSpace(text) +} diff --git a/go/copilotexperimental/experimental_test.go b/go/copilotexperimental/experimental_test.go new file mode 100644 index 000000000..cfa53b3e8 --- /dev/null +++ b/go/copilotexperimental/experimental_test.go @@ -0,0 +1,13 @@ +package copilotexperimental_test + +import ( + "testing" + + "golang.org/x/tools/go/analysis/analysistest" + + "github.com/github/copilot-sdk/go/copilotexperimental" +) + +func TestAnalyzer(t *testing.T) { + analysistest.Run(t, analysistest.TestData(), copilotexperimental.Analyzer, "sdk", "consumer") +} diff --git a/go/copilotexperimental/go.mod b/go/copilotexperimental/go.mod new file mode 100644 index 000000000..70f2ad29a --- /dev/null +++ b/go/copilotexperimental/go.mod @@ -0,0 +1,10 @@ +module github.com/github/copilot-sdk/go/copilotexperimental + +go 1.24 + +require golang.org/x/tools v0.28.0 + +require ( + golang.org/x/mod v0.22.0 // indirect + golang.org/x/sync v0.10.0 // indirect +) diff --git a/go/copilotexperimental/go.sum b/go/copilotexperimental/go.sum new file mode 100644 index 000000000..e3144c2c5 --- /dev/null +++ b/go/copilotexperimental/go.sum @@ -0,0 +1,8 @@ +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= +golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= diff --git a/go/copilotexperimental/testdata/src/consumer/consumer.go b/go/copilotexperimental/testdata/src/consumer/consumer.go new file mode 100644 index 000000000..0baca6e18 --- /dev/null +++ b/go/copilotexperimental/testdata/src/consumer/consumer.go @@ -0,0 +1,24 @@ +package consumer + +import "sdk" + +func useStable() { + _ = sdk.StableGreeting("world") + client := &sdk.Client{Name: "ok"} + client.Connect() +} + +func useExperimental() { + _ = sdk.StartCanvas() // want `experimental API 'StartCanvas'` + var options sdk.CanvasOptions // want `experimental API 'CanvasOptions'` + options.Title = "x" + _ = options + + client := &sdk.Client{} + client.EnableMCPApps = true // want `experimental API 'EnableMCPApps'` + client.EnableExperimentalMode() // want `experimental API 'EnableExperimentalMode'` +} + +func optedIn() { + _ = sdk.StartCanvas() //nolint:copilotexperimental +} diff --git a/go/copilotexperimental/testdata/src/sdk/sdk.go b/go/copilotexperimental/testdata/src/sdk/sdk.go new file mode 100644 index 000000000..ca540eca8 --- /dev/null +++ b/go/copilotexperimental/testdata/src/sdk/sdk.go @@ -0,0 +1,38 @@ +// Package sdk is a miniature stand-in for the generated Copilot SDK surface. +package sdk + +// StableGreeting is a stable API. +func StableGreeting(name string) string { + return "Hello, " + name +} + +// StartCanvas starts an experimental canvas session. +// +// Experimental: StartCanvas is an experimental API and may change or be removed. +func StartCanvas() string { // want StartCanvas:"experimental" + return "canvas" +} + +// CanvasOptions configures a canvas. +// +// Experimental: CanvasOptions is part of an experimental API and may change or be removed. +type CanvasOptions struct { // want CanvasOptions:"experimental" + Title string +} + +// Client is a stable client. +type Client struct { + // Name is a stable field. + Name string + + // Experimental: EnableMCPApps is part of an experimental wire-protocol surface and may change or be removed. + EnableMCPApps bool // want EnableMCPApps:"experimental" +} + +// Connect is a stable method. +func (c *Client) Connect() {} + +// EnableExperimentalMode enables an experimental mode. +// +// Experimental: EnableExperimentalMode is an experimental API and may change or be removed. +func (c *Client) EnableExperimentalMode() {} // want EnableExperimentalMode:"experimental" diff --git a/nodejs/README.md b/nodejs/README.md index bc91cd793..929232bef 100644 --- a/nodejs/README.md +++ b/nodejs/README.md @@ -1066,6 +1066,25 @@ const session = await client.createSession({ - `onSessionEnd` - Cleanup or logging when session ends. - `onErrorOccurred` - Handle errors with retry/skip/abort strategies. +## Experimental APIs + +Some SDK members are **experimental**: they're documented with an +`@experimental` JSDoc tag and may change or be removed in any release. Because +JSDoc tags don't produce compiler diagnostics on their own, this package ships a +companion ESLint plugin, +[`@github/eslint-plugin-copilot-sdk`](./eslint-plugin/README.md), whose +type-aware `no-experimental-api` rule flags any reference to an experimental +member — the TypeScript analog of C# `[Experimental]` / Java +`@CopilotExperimental`. Opting in is then an explicit, auditable +`eslint-disable` at the call site: + +```ts +// eslint-disable-next-line @github/copilot-sdk/no-experimental-api +session.someExperimentalApi(); +``` + +See the [plugin README](./eslint-plugin/README.md) for flat-config setup. + ## Error Handling ```typescript diff --git a/nodejs/eslint-plugin/.gitignore b/nodejs/eslint-plugin/.gitignore new file mode 100644 index 000000000..c2658d7d1 --- /dev/null +++ b/nodejs/eslint-plugin/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/nodejs/eslint-plugin/README.md b/nodejs/eslint-plugin/README.md new file mode 100644 index 000000000..4768352e2 --- /dev/null +++ b/nodejs/eslint-plugin/README.md @@ -0,0 +1,51 @@ +# `@github/eslint-plugin-copilot-sdk` + +Type-aware ESLint rules for the GitHub Copilot SDK. + +## What it does + +`@github/copilot-sdk/no-experimental-api` reports references to SDK symbols marked +with `/** @experimental */`. Consumers must explicitly suppress the diagnostic to +opt in at a call site. + +## Install + +```bash +npm install --save-dev @github/eslint-plugin-copilot-sdk @typescript-eslint/parser eslint typescript +``` + +## Flat config + +```js +import tsParser from "@typescript-eslint/parser"; +import copilotSdk from "@github/eslint-plugin-copilot-sdk"; + +export default [ + { + files: ["src/**/*.ts"], + languageOptions: { + parser: tsParser, + parserOptions: { + project: "./tsconfig.json", + tsconfigRootDir: import.meta.dirname, + }, + }, + plugins: { + "@github/copilot-sdk": copilotSdk, + }, + rules: { + ...copilotSdk.configs.recommended.rules, + }, + }, +]; +``` + +This rule requires type-aware linting, so `parserOptions.project` must point to a +TypeScript project that includes the files being linted. + +## Suppressing a single use + +```ts +// eslint-disable-next-line @github/copilot-sdk/no-experimental-api +startCanvas(); +``` diff --git a/nodejs/eslint-plugin/eslint.config.js b/nodejs/eslint-plugin/eslint.config.js new file mode 100644 index 000000000..d9efc3994 --- /dev/null +++ b/nodejs/eslint-plugin/eslint.config.js @@ -0,0 +1,21 @@ +const parser = require("@typescript-eslint/parser"); +const copilotSdk = require("./index.js"); + +module.exports = [ + { + files: ["test/fixtures/consumer-*.ts"], + languageOptions: { + parser, + parserOptions: { + project: "./test/fixtures/tsconfig.json", + tsconfigRootDir: __dirname, + }, + }, + plugins: { + "@github/copilot-sdk": copilotSdk, + }, + rules: { + "@github/copilot-sdk/no-experimental-api": "error", + }, + }, +]; diff --git a/nodejs/eslint-plugin/index.js b/nodejs/eslint-plugin/index.js new file mode 100644 index 000000000..f5c50c324 --- /dev/null +++ b/nodejs/eslint-plugin/index.js @@ -0,0 +1,33 @@ +const noExperimentalApi = require("./rules/no-experimental-api"); + +/** @type {import("eslint").ESLint.Plugin} */ +const plugin = { + meta: { + name: "@github/eslint-plugin-copilot-sdk", + version: "0.0.0", + }, + rules: { + "no-experimental-api": noExperimentalApi, + }, + configs: {}, +}; + +plugin.configs = { + recommended: { + name: "@github/copilot-sdk/recommended", + plugins: { + "@github/copilot-sdk": plugin, + }, + rules: { + "@github/copilot-sdk/no-experimental-api": "error", + }, + }, + "recommended-legacy": { + plugins: ["@github/copilot-sdk"], + rules: { + "@github/copilot-sdk/no-experimental-api": "error", + }, + }, +}; + +module.exports = plugin; diff --git a/nodejs/eslint-plugin/package-lock.json b/nodejs/eslint-plugin/package-lock.json new file mode 100644 index 000000000..581e51515 --- /dev/null +++ b/nodejs/eslint-plugin/package-lock.json @@ -0,0 +1,1382 @@ +{ + "name": "@github/eslint-plugin-copilot-sdk", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@github/eslint-plugin-copilot-sdk", + "version": "0.0.0", + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^8.54.0" + }, + "devDependencies": { + "@typescript-eslint/parser": "^8.54.0", + "eslint": "^9.0.0", + "typescript": "^5.0.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "eslint": ">=9", + "typescript": ">=5" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "license": "MIT" + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.61.1.tgz", + "integrity": "sha512-PJ5vePq5/ognBbrIcoC5+SHO5dfpeLPzP9FpLkzWrguoYQEeeSjlJpVwOpo1JRSTEi7dRcwNy4h4dzV70PqHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.61.1", + "@typescript-eslint/types": "8.61.1", + "@typescript-eslint/typescript-estree": "8.61.1", + "@typescript-eslint/visitor-keys": "8.61.1", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.61.1.tgz", + "integrity": "sha512-PrC4JYGmR241lYnfhmKGTXkFqv8+ymbTFgSAY0fVXpY82/QkMw5TZPl+vGzuDDU2QYJk9fIDOBTntF+yDv9LEA==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.61.1", + "@typescript-eslint/types": "^8.61.1", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.61.1.tgz", + "integrity": "sha512-L2bdIeoQS8FlKAvONAr20w6OcLXeB+qiDKbAooS9A0Ben+iSIkBef0FxqwKWYqt5sa0i4KJtxVyVmhMylKzF5w==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.61.1", + "@typescript-eslint/visitor-keys": "8.61.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.61.1.tgz", + "integrity": "sha512-UN/H4di+OO7EWx2ovME+8t31YO+KVnK0RRKEHR3kOt21/Ay8BOq3M1OMvWs5vNiqcFCYGYoxK3MXPZzmMUE+yg==", + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.61.1.tgz", + "integrity": "sha512-G+CRlPqLv7Bz1IZVs03x5K59F1veqL0EJUROAdGhKsEq8qOiRiZbI+HUojPq5l0fEGOKModD9br6lObhB8zkoA==", + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.61.1.tgz", + "integrity": "sha512-u+oQD3BqYWPc8YV9Zab4vaJElJuwOLPRc10Jm1o/qS+6Qwen14HCWwx0Seo4LnSn2wxea2Ik8DxPt2/FHmuhrg==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.61.1", + "@typescript-eslint/tsconfig-utils": "8.61.1", + "@typescript-eslint/types": "8.61.1", + "@typescript-eslint/visitor-keys": "8.61.1", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.61.1.tgz", + "integrity": "sha512-1+P/3Dj6jvtybE1q0HQ6yBt/gq+oKJyLdEv4HdnqasaEXRSYCAsD59mXEVQnM/ULNdQxbX77tdG4jPRjIS6knA==", + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.61.1", + "@typescript-eslint/types": "8.61.1", + "@typescript-eslint/typescript-estree": "8.61.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.61.1.tgz", + "integrity": "sha512-6fJ9MHWtK14C1DSkiMlHUSOmrVebL7150xZJBlJiL62jjhIA4JmOq6flwBgDxIdBKKdoiZRel+dfPD5MLfny3w==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.61.1", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/acorn": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "license": "ISC" + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/js-yaml": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", + "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/semver": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", + "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/nodejs/eslint-plugin/package.json b/nodejs/eslint-plugin/package.json new file mode 100644 index 000000000..d2165989f --- /dev/null +++ b/nodejs/eslint-plugin/package.json @@ -0,0 +1,50 @@ +{ + "name": "@github/eslint-plugin-copilot-sdk", + "version": "0.0.0", + "description": "ESLint plugin for detecting experimental GitHub Copilot SDK APIs.", + "main": "index.js", + "exports": { + ".": "./index.js" + }, + "scripts": { + "test": "node --test test/no-experimental-api.test.js" + }, + "repository": { + "type": "git", + "url": "https://github.com/github/copilot-sdk.git", + "directory": "nodejs/eslint-plugin" + }, + "keywords": [ + "github", + "copilot", + "eslint", + "eslintplugin", + "typescript" + ], + "author": "GitHub", + "license": "MIT", + "bugs": { + "url": "https://github.com/github/copilot-sdk/issues" + }, + "homepage": "https://github.com/github/copilot-sdk/tree/main/nodejs/eslint-plugin", + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "files": [ + "index.js", + "rules/", + "README.md" + ], + "peerDependencies": { + "eslint": ">=9", + "typescript": ">=5" + }, + "dependencies": { + "@typescript-eslint/utils": "^8.54.0" + }, + "devDependencies": { + "@typescript-eslint/parser": "^8.54.0", + "eslint": "^9.0.0", + "typescript": "^5.0.0" + } +} diff --git a/nodejs/eslint-plugin/rules/no-experimental-api.js b/nodejs/eslint-plugin/rules/no-experimental-api.js new file mode 100644 index 000000000..ac6a079af --- /dev/null +++ b/nodejs/eslint-plugin/rules/no-experimental-api.js @@ -0,0 +1,96 @@ +// @ts-check +const { ESLintUtils } = require("@typescript-eslint/utils"); +const ts = require("typescript"); + +const DOCS_URL = "https://github.com/github/copilot-sdk/tree/main/nodejs/eslint-plugin"; +const EXPERIMENTAL_TAG = "experimental"; + +const createRule = ESLintUtils.RuleCreator((name) => `${DOCS_URL}#${name}`); + +function resolveSymbol(symbol, checker) { + if (!symbol) { + return undefined; + } + + let resolvedSymbol = symbol; + const seenSymbols = new Set(); + + while ((resolvedSymbol.flags & ts.SymbolFlags.Alias) !== 0) { + if (seenSymbols.has(resolvedSymbol)) { + break; + } + + seenSymbols.add(resolvedSymbol); + + try { + resolvedSymbol = checker.getAliasedSymbol(resolvedSymbol); + } catch { + break; + } + } + + return resolvedSymbol; +} + +function getExperimentalTag(symbol, checker) { + const resolvedSymbol = resolveSymbol(symbol, checker); + return resolvedSymbol + ?.getJsDocTags(checker) + .find((tag) => tag.name === EXPERIMENTAL_TAG); +} + +function isDeclarationSite(symbol, tsNode) { + return (symbol.declarations ?? []).some((declaration) => declaration.name === tsNode); +} + +module.exports = createRule({ + name: "no-experimental-api", + meta: { + type: "problem", + docs: { + description: + "Disallow referencing @experimental Copilot SDK APIs without an explicit opt-in.", + recommended: "recommended", + requiresTypeChecking: true, + }, + messages: { + experimental: + "'{{name}}' is an experimental Copilot SDK API and may change or be removed without notice. Opt in explicitly with `// eslint-disable-next-line @github/copilot-sdk/no-experimental-api`.", + }, + schema: [], + }, + defaultOptions: [], + create(context) { + const services = ESLintUtils.getParserServices(context); + const checker = services.program.getTypeChecker(); + + function check(node) { + const tsNode = services.esTreeNodeToTSNodeMap.get(node); + if (!tsNode) { + return; + } + + const symbol = checker.getSymbolAtLocation(tsNode); + if (!symbol || isDeclarationSite(symbol, tsNode)) { + return; + } + + if (!getExperimentalTag(symbol, checker)) { + return; + } + + context.report({ + node, + messageId: "experimental", + data: { + name: node.name, + }, + }); + } + + return { + Identifier: check, + JSXIdentifier: check, + }; + }, +}); diff --git a/nodejs/eslint-plugin/test/fixtures/consumer-aliased.ts b/nodejs/eslint-plugin/test/fixtures/consumer-aliased.ts new file mode 100644 index 000000000..beabe9e90 --- /dev/null +++ b/nodejs/eslint-plugin/test/fixtures/consumer-aliased.ts @@ -0,0 +1,5 @@ +import { launchCanvas } from "./re-export"; + +const canvas = launchCanvas(); + +console.log(canvas); diff --git a/nodejs/eslint-plugin/test/fixtures/consumer-experimental.ts b/nodejs/eslint-plugin/test/fixtures/consumer-experimental.ts new file mode 100644 index 000000000..8e4cb1b04 --- /dev/null +++ b/nodejs/eslint-plugin/test/fixtures/consumer-experimental.ts @@ -0,0 +1,13 @@ +import { stableGreeting, startCanvas, CanvasOptions, StableClient } from "./sdk"; + +console.log(stableGreeting("world")); + +const canvas = startCanvas(); +console.log(canvas); + +const options: CanvasOptions = { title: "demo" }; +console.log(options); + +const client = new StableClient(); +client.greet(); +client.enableMcpApps(); diff --git a/nodejs/eslint-plugin/test/fixtures/consumer-stable.ts b/nodejs/eslint-plugin/test/fixtures/consumer-stable.ts new file mode 100644 index 000000000..c1af458f6 --- /dev/null +++ b/nodejs/eslint-plugin/test/fixtures/consumer-stable.ts @@ -0,0 +1,6 @@ +import { stableGreeting, StableClient } from "./sdk"; + +console.log(stableGreeting("world")); + +const client = new StableClient(); +client.greet(); diff --git a/nodejs/eslint-plugin/test/fixtures/consumer-suppressed.ts b/nodejs/eslint-plugin/test/fixtures/consumer-suppressed.ts new file mode 100644 index 000000000..f3f05dcdd --- /dev/null +++ b/nodejs/eslint-plugin/test/fixtures/consumer-suppressed.ts @@ -0,0 +1,6 @@ +import { startCanvas } from "./sdk"; + +// eslint-disable-next-line @github/copilot-sdk/no-experimental-api +const canvas = startCanvas(); + +console.log(canvas); diff --git a/nodejs/eslint-plugin/test/fixtures/re-export.ts b/nodejs/eslint-plugin/test/fixtures/re-export.ts new file mode 100644 index 000000000..97c35010b --- /dev/null +++ b/nodejs/eslint-plugin/test/fixtures/re-export.ts @@ -0,0 +1 @@ +export { startCanvas as launchCanvas } from "./sdk"; diff --git a/nodejs/eslint-plugin/test/fixtures/sdk.ts b/nodejs/eslint-plugin/test/fixtures/sdk.ts new file mode 100644 index 000000000..12c241c73 --- /dev/null +++ b/nodejs/eslint-plugin/test/fixtures/sdk.ts @@ -0,0 +1,34 @@ +/** A perfectly stable API. */ +export function stableGreeting(name: string): string { + return `Hello, ${name}`; +} + +/** + * Start an experimental canvas session. + * @experimental + */ +export function startCanvas(): string { + return "canvas"; +} + +/** + * Options bag for an experimental feature. + * @experimental + */ +export interface CanvasOptions { + title: string; +} + +export class StableClient { + greet(): string { + return "hi"; + } + + /** + * Enable experimental MCP apps support. + * @experimental + */ + enableMcpApps(): void { + // ... + } +} diff --git a/nodejs/eslint-plugin/test/fixtures/tsconfig.json b/nodejs/eslint-plugin/test/fixtures/tsconfig.json new file mode 100644 index 000000000..a4fe75b7e --- /dev/null +++ b/nodejs/eslint-plugin/test/fixtures/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "strict": true, + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "Bundler", + "skipLibCheck": true, + "noEmit": true + }, + "include": ["*.ts"] +} diff --git a/nodejs/eslint-plugin/test/no-experimental-api.test.js b/nodejs/eslint-plugin/test/no-experimental-api.test.js new file mode 100644 index 000000000..fbbdcd5ac --- /dev/null +++ b/nodejs/eslint-plugin/test/no-experimental-api.test.js @@ -0,0 +1,53 @@ +const assert = require("node:assert/strict"); +const path = require("node:path"); +const test = require("node:test"); +const { ESLint } = require("eslint"); + +function getResult(results, fileName) { + const result = results.find((entry) => path.basename(entry.filePath) === fileName); + assert.ok(result, `Missing ESLint result for ${fileName}`); + return result; +} + +function getReportedNames(messages) { + return messages + .map((message) => message.message.match(/'([^']+)'/)?.[1]) + .filter(Boolean) + .sort(); +} + +function assertRuleIds(messages) { + for (const message of messages) { + assert.strictEqual(message.ruleId, "@github/copilot-sdk/no-experimental-api"); + } +} + +test("flags experimental references and ignores stable or suppressed uses", async () => { + const eslint = new ESLint({ cwd: path.join(__dirname, "..") }); + const results = await eslint.lintFiles(["test/fixtures/consumer-*.ts"]); + + const experimentalMessages = getResult(results, "consumer-experimental.ts").messages; + assert.strictEqual( + experimentalMessages.length, + 3, + `expected 3 diagnostics, got:\n${JSON.stringify(experimentalMessages, null, 2)}`, + ); + assertRuleIds(experimentalMessages); + assert.deepStrictEqual(getReportedNames(experimentalMessages), [ + "CanvasOptions", + "enableMcpApps", + "startCanvas", + ]); + + assert.deepStrictEqual(getResult(results, "consumer-stable.ts").messages, []); + assert.deepStrictEqual(getResult(results, "consumer-suppressed.ts").messages, []); + + const aliasedMessages = getResult(results, "consumer-aliased.ts").messages; + assert.strictEqual( + aliasedMessages.length, + 1, + `expected 1 diagnostic, got:\n${JSON.stringify(aliasedMessages, null, 2)}`, + ); + assertRuleIds(aliasedMessages); + assert.deepStrictEqual(getReportedNames(aliasedMessages), ["launchCanvas"]); +}); diff --git a/python/copilot/__init__.py b/python/copilot/__init__.py index ff2562d68..3e2adddff 100644 --- a/python/copilot/__init__.py +++ b/python/copilot/__init__.py @@ -64,6 +64,13 @@ TelemetryConfig, UriRuntimeConnection, ) +from .experimental import ( + ExperimentalWarning, + allow_experimental, + experimental, + is_experimental, + set_experimental_policy, +) from .generated.rpc import ( ModelBillingTokenPrices, ModelBillingTokenPricesLongContext, @@ -195,6 +202,7 @@ "ExitPlanModeHandler", "ExitPlanModeRequest", "ExitPlanModeResult", + "ExperimentalWarning", "ExtensionInfo", "GetAuthStatusResponse", "GetStatusResponse", @@ -292,7 +300,11 @@ "UserPromptSubmittedHandler", "UserPromptSubmittedHookInput", "UserPromptSubmittedHookOutput", + "allow_experimental", "convert_mcp_call_tool_result", "create_session_fs_adapter", "define_tool", + "experimental", + "is_experimental", + "set_experimental_policy", ] diff --git a/python/copilot/experimental.py b/python/copilot/experimental.py new file mode 100644 index 000000000..b3e307ff3 --- /dev/null +++ b/python/copilot/experimental.py @@ -0,0 +1,189 @@ +"""Runtime gating for experimental Copilot SDK APIs. + +Python has no compile step, so the closest analog to C# ``[Experimental]`` and +Java ``@CopilotExperimental`` is a runtime diagnostic that fires when an +experimental API is used and that the consumer must explicitly silence. This +mirrors the long-established pattern used by NumPy +(``VisibleDeprecationWarning``) and matplotlib. + +Experimental SDK functions, methods, and classes are decorated with +:func:`experimental`. Using one emits an :class:`ExperimentalWarning` that +points at the calling code:: + + import warnings + + import copilot + + # Opt in for a region of code (the equivalent of suppressing the + # compiler diagnostic in C#/Java): + with copilot.allow_experimental(): + await client.some_experimental_method() + + # Opt in globally: + warnings.filterwarnings("ignore", category=copilot.ExperimentalWarning) + + # Forbid experimental APIs (recommended for CI): + copilot.set_experimental_policy("error") + # or set the COPILOT_EXPERIMENTAL=error environment variable +""" + +from __future__ import annotations + +import functools +import inspect +import os +import warnings +from collections.abc import Callable +from contextlib import contextmanager +from typing import TYPE_CHECKING, Any, Literal, TypeVar, overload + +if TYPE_CHECKING: + from collections.abc import Iterator + +__all__ = [ + "ExperimentalWarning", + "allow_experimental", + "experimental", + "is_experimental", + "set_experimental_policy", +] + +_T = TypeVar("_T") + +_MARKER = "__copilot_experimental__" + +_POLICY_ACTIONS: dict[str, Literal["default", "error", "ignore"]] = { + "warn": "default", + "error": "error", + "ignore": "ignore", +} + + +class ExperimentalWarning(Warning): + """Warning category for Copilot SDK APIs that are experimental. + + Experimental APIs may change in backwards-incompatible ways or be removed + entirely in any release, without notice and without a deprecation period. + They are surfaced as a warning (rather than hidden in docs) so that usage is + a deliberate, acknowledged choice. + """ + + +def _message(qualname: str, since: str | None) -> str: + msg = ( + f"{qualname} is an experimental Copilot SDK API and may change or be " + "removed in any release without notice." + ) + if since: + msg += f" (experimental since {since})" + msg += ( + " Silence this warning with `copilot.allow_experimental()` to acknowledge " + "that you are depending on an experimental API." + ) + return msg + + +@overload +def experimental(obj: _T) -> _T: ... + + +@overload +def experimental(*, since: str | None = ...) -> Callable[[_T], _T]: ... + + +def experimental(obj: Any = None, *, since: str | None = None) -> Any: + """Mark a function, method, or class as experimental. + + Usable bare (``@experimental``) or parameterized + (``@experimental(since="1.2")``). Calling the decorated function/method or + constructing the decorated class emits an :class:`ExperimentalWarning` + pointing at the caller. The wrapper is signature- and type-preserving, so + decorated APIs keep their original typing. + """ + + def decorate(target: Any) -> Any: + qualname = getattr(target, "__qualname__", getattr(target, "__name__", repr(target))) + message = _message(qualname, since) + + if inspect.isclass(target): + original_init = target.__init__ + + @functools.wraps(original_init) + def __init__(self: Any, *args: Any, **kwargs: Any) -> None: + warnings.warn(message, ExperimentalWarning, stacklevel=2) + original_init(self, *args, **kwargs) + + target.__init__ = __init__ + setattr(target, _MARKER, True) + return target + + if inspect.iscoroutinefunction(target): + + @functools.wraps(target) + async def async_wrapper(*args: Any, **kwargs: Any) -> Any: + warnings.warn(message, ExperimentalWarning, stacklevel=2) + return await target(*args, **kwargs) + + setattr(async_wrapper, _MARKER, True) + return async_wrapper + + @functools.wraps(target) + def wrapper(*args: Any, **kwargs: Any) -> Any: + warnings.warn(message, ExperimentalWarning, stacklevel=2) + return target(*args, **kwargs) + + setattr(wrapper, _MARKER, True) + return wrapper + + # @experimental (bare): ``obj`` is the decorated target. + if obj is not None: + return decorate(obj) + # @experimental(...): return the decorator. + return decorate + + +def is_experimental(obj: Any) -> bool: + """Return ``True`` if ``obj`` (a function, method, class, or instance) is experimental.""" + if getattr(obj, _MARKER, False): + return True + return bool(getattr(type(obj), _MARKER, False)) + + +@contextmanager +def allow_experimental() -> Iterator[None]: + """Locally silence :class:`ExperimentalWarning`. + + This is the explicit, scoped opt-in — the Python equivalent of suppressing + the compiler diagnostic in C#/Java:: + + with copilot.allow_experimental(): + await client.some_experimental_method() + """ + with warnings.catch_warnings(): + warnings.simplefilter("ignore", ExperimentalWarning) + yield + + +def set_experimental_policy(policy: str) -> None: + """Set the process-wide policy for experimental-API usage. + + * ``"warn"`` — emit an :class:`ExperimentalWarning` (the default) + * ``"error"`` — raise on use (recommended for CI to forbid experimental APIs) + * ``"ignore"`` — silence entirely (a blanket opt-in) + + This can also be driven by the ``COPILOT_EXPERIMENTAL`` environment variable, + which is applied automatically on import. + """ + normalized = policy.strip().lower() + action = _POLICY_ACTIONS.get(normalized) + if action is None: + raise ValueError( + f"unknown experimental policy: {policy!r} (expected 'warn', 'error', or 'ignore')" + ) + warnings.filterwarnings(action, category=ExperimentalWarning) + + +# Honor an environment-driven default so CI can flip an entire run to "error". +_env_policy = os.environ.get("COPILOT_EXPERIMENTAL") +if _env_policy: + set_experimental_policy(_env_policy) diff --git a/python/copilot/generated/rpc.py b/python/copilot/generated/rpc.py index 6822fd7ff..467e23462 100644 --- a/python/copilot/generated/rpc.py +++ b/python/copilot/generated/rpc.py @@ -20,6 +20,8 @@ import dateutil.parser +from ..experimental import experimental + T = TypeVar("T") EnumT = TypeVar("EnumT", bound=Enum) @@ -22722,25 +22724,30 @@ class ServerPluginsMarketplacesApi: def __init__(self, client: "JsonRpcClient"): self._client = client + @experimental async def list(self, *, timeout: float | None = None) -> MarketplaceListResult: "Lists all registered marketplaces (defaults + user-added).\n\nReturns:\n All registered marketplaces, including built-in defaults." return MarketplaceListResult.from_dict(await self._client.request("plugins.marketplaces.list", {}, **_timeout_kwargs(timeout))) + @experimental async def add(self, params: PluginsMarketplacesAddRequest, *, timeout: float | None = None) -> MarketplaceAddResult: "Registers a new marketplace from a source (owner/repo, URL, or local path).\n\nArgs:\n params: Marketplace source to register.\n\nReturns:\n Result of registering a new marketplace." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} return MarketplaceAddResult.from_dict(await self._client.request("plugins.marketplaces.add", params_dict, **_timeout_kwargs(timeout))) + @experimental async def remove(self, params: PluginsMarketplacesRemoveRequest, *, timeout: float | None = None) -> MarketplaceRemoveResult: "Removes a previously-registered marketplace. When the marketplace has dependent plugins and `force` is not set, the marketplace is left intact and the result lists the dependents so the caller can decide whether to retry with `force=true`.\n\nArgs:\n params: Name of the marketplace to remove and an optional force flag.\n\nReturns:\n Outcome of the remove attempt, including dependent-plugin info when applicable." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} return MarketplaceRemoveResult.from_dict(await self._client.request("plugins.marketplaces.remove", params_dict, **_timeout_kwargs(timeout))) + @experimental async def browse(self, params: PluginsMarketplacesBrowseRequest, *, timeout: float | None = None) -> MarketplaceBrowseResult: "Lists plugins advertised by a registered marketplace.\n\nArgs:\n params: Name of the marketplace whose plugin catalog to fetch.\n\nReturns:\n Plugins advertised by the marketplace." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} return MarketplaceBrowseResult.from_dict(await self._client.request("plugins.marketplaces.browse", params_dict, **_timeout_kwargs(timeout))) + @experimental async def refresh(self, params: PluginsMarketplacesRefreshRequest, *, timeout: float | None = None) -> MarketplaceRefreshResult: "Re-fetches one or all registered marketplace catalogs.\n\nArgs:\n params: Optional marketplace name; omit to refresh all.\n\nReturns:\n Result of refreshing one or more marketplace catalogs." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} @@ -22753,34 +22760,41 @@ def __init__(self, client: "JsonRpcClient"): self._client = client self.marketplaces = ServerPluginsMarketplacesApi(client) + @experimental async def list(self, *, timeout: float | None = None) -> PluginListResult: "Lists plugins installed in user/global state.\n\nReturns:\n Plugins installed in user/global state." return PluginListResult.from_dict(await self._client.request("plugins.list", {}, **_timeout_kwargs(timeout))) + @experimental async def install(self, params: PluginsInstallRequest, *, timeout: float | None = None) -> PluginInstallResult: "Installs a plugin from a marketplace, GitHub repo, URL, or local path.\n\nArgs:\n params: Plugin source and optional working directory for relative-path resolution.\n\nReturns:\n Result of installing a plugin." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} return PluginInstallResult.from_dict(await self._client.request("plugins.install", params_dict, **_timeout_kwargs(timeout))) + @experimental async def uninstall(self, params: PluginsUninstallRequest, *, timeout: float | None = None) -> None: "Uninstalls an installed plugin.\n\nArgs:\n params: Name (or spec) of the plugin to uninstall." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} await self._client.request("plugins.uninstall", params_dict, **_timeout_kwargs(timeout)) + @experimental async def update(self, params: PluginsUpdateRequest, *, timeout: float | None = None) -> PluginUpdateResult: "Updates an installed plugin to its latest published version.\n\nArgs:\n params: Name (or spec) of the plugin to update.\n\nReturns:\n Result of updating a single plugin." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} return PluginUpdateResult.from_dict(await self._client.request("plugins.update", params_dict, **_timeout_kwargs(timeout))) + @experimental async def update_all(self, *, timeout: float | None = None) -> PluginUpdateAllResult: "Updates every installed plugin to its latest published version.\n\nReturns:\n Result of updating all installed plugins." return PluginUpdateAllResult.from_dict(await self._client.request("plugins.updateAll", {}, **_timeout_kwargs(timeout))) + @experimental async def enable(self, params: PluginsEnableRequest, *, timeout: float | None = None) -> None: "Enables installed plugins for new sessions.\n\nArgs:\n params: Plugin names (or specs) to enable." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} await self._client.request("plugins.enable", params_dict, **_timeout_kwargs(timeout)) + @experimental async def disable(self, params: PluginsDisableRequest, *, timeout: float | None = None) -> None: "Disables installed plugins for new sessions.\n\nArgs:\n params: Plugin names (or specs) to disable." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} @@ -22807,6 +22821,7 @@ async def discover(self, params: SkillsDiscoverRequest, *, timeout: float | None params_dict = {k: v for k, v in params.to_dict().items() if v is not None} return ServerSkillList.from_dict(await self._client.request("skills.discover", params_dict, **_timeout_kwargs(timeout))) + @experimental async def get_discovery_paths(self, params: SkillsGetDiscoveryPathsRequest, *, timeout: float | None = None) -> SkillDiscoveryPathList: "Returns the canonical directories where a client may create skills that the runtime will recognize, including ones that do not exist yet. Project directories become active once created.\n\nArgs:\n params: Optional project paths to enumerate.\n\nReturns:\n Canonical locations where skills can be created so the runtime will recognize them.\n\n.. warning:: This API is experimental and may change or be removed in future versions." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} @@ -22818,11 +22833,13 @@ class ServerAgentsApi: def __init__(self, client: "JsonRpcClient"): self._client = client + @experimental async def discover(self, params: AgentsDiscoverRequest, *, timeout: float | None = None) -> ServerAgentList: "Discovers custom agents across user, project, plugin, and remote sources.\n\nArgs:\n params: Optional project paths to include in agent discovery.\n\nReturns:\n Agents discovered across user, project, plugin, and remote sources." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} return ServerAgentList.from_dict(await self._client.request("agents.discover", params_dict, **_timeout_kwargs(timeout))) + @experimental async def get_discovery_paths(self, params: AgentsGetDiscoveryPathsRequest, *, timeout: float | None = None) -> AgentDiscoveryPathList: "Returns the canonical directories where a client may create custom agents that the runtime will recognize, including ones that do not exist yet. Project directories become active once created.\n\nArgs:\n params: Optional project paths to include when enumerating agent discovery directories.\n\nReturns:\n Canonical locations where custom agents can be created so the runtime will recognize them." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} @@ -22834,11 +22851,13 @@ class ServerInstructionsApi: def __init__(self, client: "JsonRpcClient"): self._client = client + @experimental async def discover(self, params: InstructionsDiscoverRequest, *, timeout: float | None = None) -> ServerInstructionSourceList: "Discovers instruction sources across user, repository, and plugin sources.\n\nArgs:\n params: Optional project paths to include in instruction discovery.\n\nReturns:\n Instruction sources discovered across user, repository, and plugin sources." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} return ServerInstructionSourceList.from_dict(await self._client.request("instructions.discover", params_dict, **_timeout_kwargs(timeout))) + @experimental async def get_discovery_paths(self, params: InstructionsGetDiscoveryPathsRequest, *, timeout: float | None = None) -> InstructionDiscoveryPathList: "Returns the canonical files and directories where a client may create custom instructions that the runtime will recognize, including ones that do not exist yet. Repository targets become active once created.\n\nArgs:\n params: Optional project paths to include when enumerating instruction discovery targets.\n\nReturns:\n Canonical files and directories where custom instructions can be created so the runtime will recognize them." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} @@ -22884,115 +22903,138 @@ class ServerSessionsApi: def __init__(self, client: "JsonRpcClient"): self._client = client + @experimental async def open(self, params: SessionOpenParams, *, timeout: float | None = None) -> SessionOpenResult: "Creates or resumes a local session and returns the opened session ID.\n\nArgs:\n params: Open a session by creating, resuming, attaching, connecting to a remote, or handing off.\n\nReturns:\n Result of opening a session." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} return SessionOpenResult.from_dict(await self._client.request("sessions.open", params_dict, **_timeout_kwargs(timeout))) + @experimental async def fork(self, params: SessionsForkRequest, *, timeout: float | None = None) -> SessionsForkResult: "Creates a new session by forking persisted history from an existing session.\n\nArgs:\n params: Source session identifier to fork from, optional event-ID boundary, and optional friendly name for the new session.\n\nReturns:\n Identifier and optional friendly name assigned to the newly forked session." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} return SessionsForkResult.from_dict(await self._client.request("sessions.fork", params_dict, **_timeout_kwargs(timeout))) + @experimental async def connect(self, params: ConnectRemoteSessionParams, *, timeout: float | None = None) -> RemoteSessionConnectionResult: "Connects to an existing remote session and exposes it as an SDK session.\n\nArgs:\n params: Remote session connection parameters.\n\nReturns:\n Remote session connection result." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} return RemoteSessionConnectionResult.from_dict(await self._client.request("sessions.connect", params_dict, **_timeout_kwargs(timeout))) + @experimental async def list(self, params: SessionsListRequest, *, timeout: float | None = None) -> SessionList: "Lists sessions, optionally filtered by source and working-directory context. Returned entries are discriminated by `isRemote`: local entries carry only the lightweight `LocalSessionMetadataValue` shape; remote entries carry the full `RemoteSessionMetadataValue` shape (repository, PR number, taskType, etc.).\n\nArgs:\n params: Optional source filter, metadata-load limit, and context filter applied to the returned sessions.\n\nReturns:\n Sessions matching the filter, ordered most-recently-modified first." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} return SessionList.from_dict(await self._client.request("sessions.list", params_dict, **_timeout_kwargs(timeout))) + @experimental async def find_by_task_id(self, params: SessionsFindByTaskIDRequest, *, timeout: float | None = None) -> SessionsFindByTaskIDResult: "Finds the local session bound to a GitHub task ID, if any.\n\nArgs:\n params: GitHub task ID to look up.\n\nReturns:\n ID of the local session bound to the given GitHub task, or omitted when none." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} return SessionsFindByTaskIDResult.from_dict(await self._client.request("sessions.findByTaskId", params_dict, **_timeout_kwargs(timeout))) + @experimental async def find_by_prefix(self, params: SessionsFindByPrefixRequest, *, timeout: float | None = None) -> SessionsFindByPrefixResult: "Resolves a UUID prefix to a unique session ID, if exactly one session matches.\n\nArgs:\n params: UUID prefix to resolve to a unique session ID.\n\nReturns:\n Session ID matching the prefix, omitted when no unique match exists." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} return SessionsFindByPrefixResult.from_dict(await self._client.request("sessions.findByPrefix", params_dict, **_timeout_kwargs(timeout))) + @experimental async def get_last_for_context(self, params: SessionsGetLastForContextRequest, *, timeout: float | None = None) -> SessionsGetLastForContextResult: "Returns the most-relevant prior session for a given working-directory context.\n\nArgs:\n params: Optional working-directory context used to score session relevance.\n\nReturns:\n Most-relevant session ID for the supplied context, or omitted when no sessions exist." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} return SessionsGetLastForContextResult.from_dict(await self._client.request("sessions.getLastForContext", params_dict, **_timeout_kwargs(timeout))) + @experimental async def get_sizes(self, *, timeout: float | None = None) -> SessionSizes: "Returns the on-disk byte size of each session's workspace directory.\n\nReturns:\n Map of sessionId -> on-disk size in bytes for each session's workspace directory." return SessionSizes.from_dict(await self._client.request("sessions.getSizes", {}, **_timeout_kwargs(timeout))) + @experimental async def check_in_use(self, params: SessionsCheckInUseRequest, *, timeout: float | None = None) -> SessionsCheckInUseResult: "Returns the subset of the supplied session IDs that are currently held by another running process.\n\nArgs:\n params: Session IDs to test for live in-use locks.\n\nReturns:\n Session IDs from the input set that are currently in use by another process." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} return SessionsCheckInUseResult.from_dict(await self._client.request("sessions.checkInUse", params_dict, **_timeout_kwargs(timeout))) + @experimental async def close(self, params: SessionsCloseRequest, *, timeout: float | None = None) -> SessionsCloseResult: "Closes a session: emits shutdown, flushes pending events, releases the in-use lock, and disposes the active session.\n\nArgs:\n params: Session ID to close.\n\nReturns:\n Closes a session: emits shutdown, flushes pending events to disk, releases the in-use lock, disposes the active session. Idempotent: succeeds even if the session is not currently active." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} return SessionsCloseResult.from_dict(await self._client.request("sessions.close", params_dict, **_timeout_kwargs(timeout))) + @experimental async def bulk_delete(self, params: SessionsBulkDeleteRequest, *, timeout: float | None = None) -> SessionBulkDeleteResult: "Closes, deactivates, and deletes a set of sessions, returning the bytes freed per session.\n\nArgs:\n params: Session IDs to close, deactivate, and delete from disk.\n\nReturns:\n Map of sessionId -> bytes freed by removing the session's workspace directory." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} return SessionBulkDeleteResult.from_dict(await self._client.request("sessions.bulkDelete", params_dict, **_timeout_kwargs(timeout))) + @experimental async def prune_old(self, params: SessionsPruneOldRequest, *, timeout: float | None = None) -> SessionPruneResult: "Deletes sessions older than the given threshold, with optional dry-run and exclusion list.\n\nArgs:\n params: Age threshold and optional flags controlling which old sessions are pruned (or simulated when dryRun is true).\n\nReturns:\n Outcome of the prune operation: deleted IDs, dry-run candidates, skipped IDs, total bytes freed, and the dry-run flag." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} return SessionPruneResult.from_dict(await self._client.request("sessions.pruneOld", params_dict, **_timeout_kwargs(timeout))) + @experimental async def save(self, params: SessionsSaveRequest, *, timeout: float | None = None) -> SessionsSaveResult: "Flushes a session's pending events to disk.\n\nArgs:\n params: Session ID whose pending events should be flushed to disk.\n\nReturns:\n Flush a session's pending events to disk. No-op when no writer exists for the session (e.g., already closed)." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} return SessionsSaveResult.from_dict(await self._client.request("sessions.save", params_dict, **_timeout_kwargs(timeout))) + @experimental async def release_lock(self, params: SessionsReleaseLockRequest, *, timeout: float | None = None) -> SessionsReleaseLockResult: "Releases the in-use lock held by this process for a session.\n\nArgs:\n params: Session ID whose in-use lock should be released.\n\nReturns:\n Release the in-use lock held by this process for the given session. No-op when this process does not currently hold a lock for the session." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} return SessionsReleaseLockResult.from_dict(await self._client.request("sessions.releaseLock", params_dict, **_timeout_kwargs(timeout))) + @experimental async def enrich_metadata(self, params: SessionsEnrichMetadataRequest, *, timeout: float | None = None) -> SessionEnrichMetadataResult: "Backfills missing summary and context fields on the supplied session metadata records.\n\nArgs:\n params: Session metadata records to enrich with summary and context information.\n\nReturns:\n The enriched metadata records, with summary and context fields backfilled where available. Sessions confirmed empty and unnamed are omitted." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} return SessionEnrichMetadataResult.from_dict(await self._client.request("sessions.enrichMetadata", params_dict, **_timeout_kwargs(timeout))) + @experimental async def reload_plugin_hooks(self, params: SessionsReloadPluginHooksRequest, *, timeout: float | None = None) -> SessionsReloadPluginHooksResult: "Reloads user, plugin, and (optionally) repo hooks on the active session.\n\nArgs:\n params: Active session ID and an optional flag for deferring repo-level hooks until folder trust.\n\nReturns:\n Reload all hooks (user, plugin, optionally repo) and apply them to the active session. Call after installing or removing plugins so their hooks take effect immediately. No-op when no active session matches the given sessionId." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} return SessionsReloadPluginHooksResult.from_dict(await self._client.request("sessions.reloadPluginHooks", params_dict, **_timeout_kwargs(timeout))) + @experimental async def load_deferred_repo_hooks(self, params: SessionsLoadDeferredRepoHooksRequest, *, timeout: float | None = None) -> SessionLoadDeferredRepoHooksResult: "Loads previously-deferred repo-level hooks on the active session, returning queued startup prompts.\n\nArgs:\n params: Active session ID whose deferred repo-level hooks should be loaded.\n\nReturns:\n Queued repo-level startup prompts and the total hook command count after loading." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} return SessionLoadDeferredRepoHooksResult.from_dict(await self._client.request("sessions.loadDeferredRepoHooks", params_dict, **_timeout_kwargs(timeout))) + @experimental async def set_additional_plugins(self, params: SessionsSetAdditionalPluginsRequest, *, timeout: float | None = None) -> SessionsSetAdditionalPluginsResult: "Replaces the manager-wide additional plugins registered with the session manager.\n\nArgs:\n params: Manager-wide additional plugins to register; replaces any previously-configured set.\n\nReturns:\n Replace the manager-wide additional plugins. New session creations and subsequent hook reloads see the new set; already-running sessions keep their existing hook installation until the next reload." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} return SessionsSetAdditionalPluginsResult.from_dict(await self._client.request("sessions.setAdditionalPlugins", params_dict, **_timeout_kwargs(timeout))) + @experimental async def start_remote_control(self, params: SessionsStartRemoteControlRequest, *, timeout: float | None = None) -> RemoteControlStatusResult: "Attaches the runtime-managed remote-control singleton to a session, awaiting initial setup. If remote control is already attached to a different session, the singleton is transferred (preserving the underlying Mission Control connection). Returns the final status.\n\nArgs:\n params: Parameters for attaching the remote-control singleton to a session.\n\nReturns:\n Wrapper for the singleton's current status." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} return RemoteControlStatusResult.from_dict(await self._client.request("sessions.startRemoteControl", params_dict, **_timeout_kwargs(timeout))) + @experimental async def transfer_remote_control(self, params: SessionsTransferRemoteControlRequest, *, timeout: float | None = None) -> RemoteControlTransferResult: "Atomically rebinds the remote-control singleton to a different session, preserving the underlying Mission Control connection. When `expectedFromSessionId` is provided and does not match the singleton's current `attachedSessionId`, the transfer is rejected with `transferred: false` and the current status is returned unchanged.\n\nArgs:\n params: Parameters for atomically rebinding the remote-control singleton.\n\nReturns:\n Outcome of a transferRemoteControl call." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} return RemoteControlTransferResult.from_dict(await self._client.request("sessions.transferRemoteControl", params_dict, **_timeout_kwargs(timeout))) + @experimental async def set_remote_control_steering(self, params: SessionsSetRemoteControlSteeringRequest, *, timeout: float | None = None) -> RemoteControlStatusResult: "Patches the steering state of the active remote-control singleton. When remote control is off, this is a no-op and the off status is returned. Today only `enabled: true` is actionable on the underlying exporter; passing `false` is reserved for future use.\n\nArgs:\n params: Patch for the singleton's steering state.\n\nReturns:\n Wrapper for the singleton's current status." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} return RemoteControlStatusResult.from_dict(await self._client.request("sessions.setRemoteControlSteering", params_dict, **_timeout_kwargs(timeout))) + @experimental async def stop_remote_control(self, params: SessionsStopRemoteControlRequest, *, timeout: float | None = None) -> RemoteControlStopResult: "Stops the remote-control singleton. When `expectedSessionId` is provided and does not match the singleton's current `attachedSessionId`, the stop is rejected with `stopped: false` and the current status is returned unchanged (unless `force` is set, in which case the singleton is unconditionally torn down).\n\nArgs:\n params: Parameters for stopping the remote-control singleton.\n\nReturns:\n Outcome of a stopRemoteControl call." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} return RemoteControlStopResult.from_dict(await self._client.request("sessions.stopRemoteControl", params_dict, **_timeout_kwargs(timeout))) + @experimental async def get_remote_control_status(self, *, timeout: float | None = None) -> RemoteControlStatusResult: "Returns the current state of the remote-control singleton, including the attached session id and frontend URL when active.\n\nReturns:\n Wrapper for the singleton's current status." return RemoteControlStatusResult.from_dict(await self._client.request("sessions.getRemoteControlStatus", {}, **_timeout_kwargs(timeout))) @@ -23003,6 +23045,7 @@ class ServerAgentRegistryApi: def __init__(self, client: "JsonRpcClient"): self._client = client + @experimental async def spawn(self, params: AgentRegistrySpawnRequest, *, timeout: float | None = None) -> AgentRegistrySpawnResult: "Spawns a managed-server child with the supplied configuration and returns a discriminated-union result. The caller (typically the CLI controller) is responsible for attaching to the spawned child and sending any follow-up prompt. When the controller-local spawn gate is closed the server returns JSON-RPC MethodNotFound.\n\nArgs:\n params: Inputs to spawn a managed-server child via the controller's spawn delegate.\n\nReturns:\n Outcome of an agentRegistry.spawn call." params_dict = {k: v for k, v in params.to_dict().items() if v is not None} @@ -23088,10 +23131,12 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def get_status(self, *, timeout: float | None = None) -> SessionAuthStatus: "Gets authentication status and account metadata for the session.\n\nReturns:\n Authentication status and account metadata for the session." return SessionAuthStatus.from_dict(await self._client.request("session.auth.getStatus", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def set_credentials(self, params: SessionSetCredentialsParams, *, timeout: float | None = None) -> SessionSetCredentialsResult: "Updates the session's auth credentials used for outbound model and API requests.\n\nArgs:\n params: New auth credentials to install on the session. Omit to leave credentials unchanged.\n\nReturns:\n Indicates whether the credential update succeeded." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} @@ -23105,6 +23150,7 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def invoke(self, params: CanvasActionInvokeRequest, *, timeout: float | None = None) -> Any: "Invokes an action on an open canvas instance.\n\nArgs:\n params: Canvas action invocation parameters.\n\nReturns:\n Canvas action invocation result." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} @@ -23119,20 +23165,24 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._session_id = session_id self.action = CanvasActionApi(client, session_id) + @experimental async def list(self, *, timeout: float | None = None) -> CanvasList: "Lists canvases declared for the session.\n\nReturns:\n Declared canvases available in this session." return CanvasList.from_dict(await self._client.request("session.canvas.list", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def list_open(self, *, timeout: float | None = None) -> CanvasListOpenResult: "Lists currently open canvas instances for the live session.\n\nReturns:\n Live open-canvas snapshot." return CanvasListOpenResult.from_dict(await self._client.request("session.canvas.listOpen", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def open(self, params: CanvasOpenRequest, *, timeout: float | None = None) -> OpenCanvasInstance: "Opens or focuses a canvas instance.\n\nArgs:\n params: Canvas open parameters.\n\nReturns:\n Open canvas instance snapshot." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return OpenCanvasInstance.from_dict(await self._client.request("session.canvas.open", params_dict, **_timeout_kwargs(timeout))) + @experimental async def close(self, params: CanvasCloseRequest, *, timeout: float | None = None) -> None: "Closes an open canvas instance.\n\nArgs:\n params: Canvas close parameters." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} @@ -23146,22 +23196,26 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def get_current(self, *, timeout: float | None = None) -> CurrentModel: "Gets the currently selected model for the session.\n\nReturns:\n The currently selected model, reasoning effort, and context tier for the session. The context tier reflects `Session.getContextTier()`, restored from the session journal on resume." return CurrentModel.from_dict(await self._client.request("session.model.getCurrent", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def switch_to(self, params: ModelSwitchToRequest, *, timeout: float | None = None) -> ModelSwitchToResult: "Switches the session to a model and optional reasoning configuration.\n\nArgs:\n params: Target model identifier and optional reasoning effort, summary, capability overrides, and context tier.\n\nReturns:\n The model identifier active on the session after the switch." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return ModelSwitchToResult.from_dict(await self._client.request("session.model.switchTo", params_dict, **_timeout_kwargs(timeout))) + @experimental async def set_reasoning_effort(self, params: ModelSetReasoningEffortRequest, *, timeout: float | None = None) -> ModelSetReasoningEffortResult: "Updates the session's reasoning effort without changing the selected model.\n\nArgs:\n params: Reasoning effort level to apply to the currently selected model.\n\nReturns:\n Update the session's reasoning effort without changing the selected model. Use `switchTo` instead when you also need to change the model. The runtime stores the effort on the session and applies it to subsequent turns." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return ModelSetReasoningEffortResult.from_dict(await self._client.request("session.model.setReasoningEffort", params_dict, **_timeout_kwargs(timeout))) + @experimental async def list(self, params: ModelListRequest | None = None, *, timeout: float | None = None) -> SessionModelList: "Lists models available to this session using its own auth and integration context. Connected hosts (CLI TUI, GitHub App) should call this through the session client so remote sessions return the remote CLI's available models rather than the caller's.\n\nArgs:\n params: Optional listing options.\n\nReturns:\n The list of models available to this session." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} if params is not None else {} @@ -23175,10 +23229,12 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def get(self, *, timeout: float | None = None) -> SessionMode: "Gets the current agent interaction mode.\n\nReturns:\n The session mode the agent is operating in" return SessionMode(await self._client.request("session.mode.get", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def set(self, params: ModeSetRequest, *, timeout: float | None = None) -> None: "Sets the current agent interaction mode.\n\nArgs:\n params: Agent interaction mode to apply to the session." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} @@ -23192,16 +23248,19 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def get(self, *, timeout: float | None = None) -> NameGetResult: "Gets the session's friendly name.\n\nReturns:\n The session's friendly name, or null when not yet set." return NameGetResult.from_dict(await self._client.request("session.name.get", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def set(self, params: NameSetRequest, *, timeout: float | None = None) -> None: "Sets the session's friendly name.\n\nArgs:\n params: New friendly name to apply to the session." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id await self._client.request("session.name.set", params_dict, **_timeout_kwargs(timeout)) + @experimental async def set_auto(self, params: NameSetAutoRequest, *, timeout: float | None = None) -> NameSetAutoResult: "Persists an auto-generated session summary as the session's name when no user-set name exists.\n\nArgs:\n params: Auto-generated session summary to apply as the session's name when no user-set name exists.\n\nReturns:\n Indicates whether the auto-generated summary was applied as the session's name." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} @@ -23215,24 +23274,29 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def read(self, *, timeout: float | None = None) -> PlanReadResult: "Reads the session plan file from the workspace.\n\nReturns:\n Existence, contents, and resolved path of the session plan file." return PlanReadResult.from_dict(await self._client.request("session.plan.read", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def update(self, params: PlanUpdateRequest, *, timeout: float | None = None) -> None: "Writes new content to the session plan file.\n\nArgs:\n params: Replacement contents to write to the session plan file." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id await self._client.request("session.plan.update", params_dict, **_timeout_kwargs(timeout)) + @experimental async def delete(self, *, timeout: float | None = None) -> None: "Deletes the session plan file from the workspace." await self._client.request("session.plan.delete", {"sessionId": self._session_id}, **_timeout_kwargs(timeout)) + @experimental async def read_sql_todos(self, *, timeout: float | None = None) -> PlanReadSQLTodosResult: "Reads todo rows from the session SQL database for plan rendering.\n\nReturns:\n Todo rows read from the session SQL database. Empty when no session database is available." return PlanReadSQLTodosResult.from_dict(await self._client.request("session.plan.readSqlTodos", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def read_sql_todos_with_dependencies(self, *, timeout: float | None = None) -> PlanReadSQLTodosWithDependenciesResult: "Reads todo rows AND dependency edges from the session SQL database for structured progress UI. Same defensive behavior as readSqlTodos — returns empty arrays when the database, tables, or columns aren't available. Clients should call this on session start and after every `session.todos_changed` event to refresh structured-UI rendering.\n\nReturns:\n Todo rows + dependency edges read from the session SQL database." return PlanReadSQLTodosWithDependenciesResult.from_dict(await self._client.request("session.plan.readSqlTodosWithDependencies", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) @@ -23244,42 +23308,50 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def get_workspace(self, *, timeout: float | None = None) -> WorkspacesGetWorkspaceResult: "Gets current workspace metadata for the session.\n\nReturns:\n Current workspace metadata for the session, including its absolute filesystem path when available." return WorkspacesGetWorkspaceResult.from_dict(await self._client.request("session.workspaces.getWorkspace", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def list_files(self, *, timeout: float | None = None) -> WorkspacesListFilesResult: "Lists files stored in the session workspace files directory.\n\nReturns:\n Relative paths of files stored in the session workspace files directory." return WorkspacesListFilesResult.from_dict(await self._client.request("session.workspaces.listFiles", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def read_file(self, params: WorkspacesReadFileRequest, *, timeout: float | None = None) -> WorkspacesReadFileResult: "Reads a file from the session workspace files directory.\n\nArgs:\n params: Relative path of the workspace file to read.\n\nReturns:\n Contents of the requested workspace file as a UTF-8 string." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return WorkspacesReadFileResult.from_dict(await self._client.request("session.workspaces.readFile", params_dict, **_timeout_kwargs(timeout))) + @experimental async def create_file(self, params: WorkspacesCreateFileRequest, *, timeout: float | None = None) -> None: "Creates or overwrites a file in the session workspace files directory.\n\nArgs:\n params: Relative path and UTF-8 content for the workspace file to create or overwrite." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id await self._client.request("session.workspaces.createFile", params_dict, **_timeout_kwargs(timeout)) + @experimental async def list_checkpoints(self, *, timeout: float | None = None) -> WorkspacesListCheckpointsResult: "Lists workspace checkpoints in chronological order.\n\nReturns:\n Workspace checkpoints in chronological order; empty when the workspace is not enabled." return WorkspacesListCheckpointsResult.from_dict(await self._client.request("session.workspaces.listCheckpoints", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def read_checkpoint(self, params: WorkspacesReadCheckpointRequest, *, timeout: float | None = None) -> WorkspacesReadCheckpointResult: "Reads the content of a workspace checkpoint by number.\n\nArgs:\n params: Checkpoint number to read.\n\nReturns:\n Checkpoint content as a UTF-8 string, or null when the checkpoint or workspace is missing." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return WorkspacesReadCheckpointResult.from_dict(await self._client.request("session.workspaces.readCheckpoint", params_dict, **_timeout_kwargs(timeout))) + @experimental async def save_large_paste(self, params: WorkspacesSaveLargePasteRequest, *, timeout: float | None = None) -> WorkspacesSaveLargePasteResult: "Saves pasted content as a UTF-8 file in the session workspace.\n\nArgs:\n params: Pasted content to save as a UTF-8 file in the session workspace.\n\nReturns:\n Descriptor for the saved paste file, or null when the workspace is unavailable." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return WorkspacesSaveLargePasteResult.from_dict(await self._client.request("session.workspaces.saveLargePaste", params_dict, **_timeout_kwargs(timeout))) + @experimental async def diff(self, params: WorkspacesDiffRequest, *, timeout: float | None = None) -> WorkspaceDiffResult: "Computes a diff for the session workspace.\n\nArgs:\n params: Parameters for computing a workspace diff.\n\nReturns:\n Workspace diff result for the requested mode." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} @@ -23293,6 +23365,7 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def get_sources(self, *, timeout: float | None = None) -> InstructionsGetSourcesResult: "Gets instruction sources loaded for the session.\n\nReturns:\n Instruction sources loaded for the session, in merge order." return InstructionsGetSourcesResult.from_dict(await self._client.request("session.instructions.getSources", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) @@ -23304,6 +23377,7 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def start(self, params: FleetStartRequest, *, timeout: float | None = None) -> FleetStartResult: "Starts fleet mode by submitting the fleet orchestration prompt to the session.\n\nArgs:\n params: Optional user prompt to combine with the fleet orchestration instructions.\n\nReturns:\n Indicates whether fleet mode was successfully activated." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} @@ -23317,24 +23391,29 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def list(self, *, timeout: float | None = None) -> AgentList: "Lists custom agents available to the session.\n\nReturns:\n Custom agents available to the session." return AgentList.from_dict(await self._client.request("session.agent.list", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def get_current(self, *, timeout: float | None = None) -> AgentGetCurrentResult: "Gets the currently selected custom agent for the session.\n\nReturns:\n The currently selected custom agent, or null when using the default agent." return AgentGetCurrentResult.from_dict(await self._client.request("session.agent.getCurrent", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def select(self, params: AgentSelectRequest, *, timeout: float | None = None) -> AgentSelectResult: "Selects a custom agent for subsequent turns in the session.\n\nArgs:\n params: Name of the custom agent to select for subsequent turns.\n\nReturns:\n The newly selected custom agent." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return AgentSelectResult.from_dict(await self._client.request("session.agent.select", params_dict, **_timeout_kwargs(timeout))) + @experimental async def deselect(self, *, timeout: float | None = None) -> None: "Clears the selected custom agent and returns the session to the default agent." await self._client.request("session.agent.deselect", {"sessionId": self._session_id}, **_timeout_kwargs(timeout)) + @experimental async def reload(self, *, timeout: float | None = None) -> AgentReloadResult: "Reloads custom agent definitions and returns the refreshed list.\n\nReturns:\n Custom agents available to the session after reloading definitions from disk." return AgentReloadResult.from_dict(await self._client.request("session.agent.reload", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) @@ -23346,56 +23425,67 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def start_agent(self, params: TasksStartAgentRequest, *, timeout: float | None = None) -> TasksStartAgentResult: "Starts a background agent task in the session.\n\nArgs:\n params: Agent type, prompt, name, and optional description and model override for the new task.\n\nReturns:\n Identifier assigned to the newly started background agent task." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return TasksStartAgentResult.from_dict(await self._client.request("session.tasks.startAgent", params_dict, **_timeout_kwargs(timeout))) + @experimental async def list(self, *, timeout: float | None = None) -> TaskList: "Lists background tasks tracked by the session.\n\nReturns:\n Background tasks currently tracked by the session." return TaskList.from_dict(await self._client.request("session.tasks.list", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def refresh(self, *, timeout: float | None = None) -> TasksRefreshResult: "Refreshes metadata for any detached background shells the runtime knows about.\n\nReturns:\n Refresh metadata for any detached background shells the runtime knows about. Use after a long pause to pick up exit/output state for shells running outside the agent loop." return TasksRefreshResult.from_dict(await self._client.request("session.tasks.refresh", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def wait_for_pending(self, *, timeout: float | None = None) -> TasksWaitForPendingResult: "Waits for all in-flight background tasks and any follow-up turns to settle.\n\nReturns:\n Wait until all in-flight background tasks (agents + shells) and any follow-up turns scheduled by their completions have settled. Returns when the runtime is fully drained or after an internal timeout (default 10 minutes; configurable via COPILOT_TASK_WAIT_TIMEOUT_SECONDS)." return TasksWaitForPendingResult.from_dict(await self._client.request("session.tasks.waitForPending", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def get_progress(self, params: TasksGetProgressRequest, *, timeout: float | None = None) -> TasksGetProgressResult: "Returns progress information for a background task by ID.\n\nArgs:\n params: Identifier of the background task to fetch progress for.\n\nReturns:\n Progress information for the task, or null when no task with that ID is tracked." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return TasksGetProgressResult.from_dict(await self._client.request("session.tasks.getProgress", params_dict, **_timeout_kwargs(timeout))) + @experimental async def get_current_promotable(self, *, timeout: float | None = None) -> TasksGetCurrentPromotableResult: "Returns the first sync-waiting task that can currently be promoted to background mode.\n\nReturns:\n The first sync-waiting task that can currently be promoted to background mode." return TasksGetCurrentPromotableResult.from_dict(await self._client.request("session.tasks.getCurrentPromotable", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def promote_to_background(self, params: TasksPromoteToBackgroundRequest, *, timeout: float | None = None) -> TasksPromoteToBackgroundResult: "Promotes an eligible synchronously-waited task so it continues running in the background.\n\nArgs:\n params: Identifier of the task to promote to background mode.\n\nReturns:\n Indicates whether the task was successfully promoted to background mode." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return TasksPromoteToBackgroundResult.from_dict(await self._client.request("session.tasks.promoteToBackground", params_dict, **_timeout_kwargs(timeout))) + @experimental async def promote_current_to_background(self, *, timeout: float | None = None) -> TasksPromoteCurrentToBackgroundResult: "Atomically promotes the first promotable sync-waiting task to background mode and returns it.\n\nReturns:\n The promoted task as it now exists in background mode, omitted if no promotable task was waiting." return TasksPromoteCurrentToBackgroundResult.from_dict(await self._client.request("session.tasks.promoteCurrentToBackground", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def cancel(self, params: TasksCancelRequest, *, timeout: float | None = None) -> TasksCancelResult: "Cancels a background task.\n\nArgs:\n params: Identifier of the background task to cancel.\n\nReturns:\n Indicates whether the background task was successfully cancelled." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return TasksCancelResult.from_dict(await self._client.request("session.tasks.cancel", params_dict, **_timeout_kwargs(timeout))) + @experimental async def remove(self, params: TasksRemoveRequest, *, timeout: float | None = None) -> TasksRemoveResult: "Removes a completed or cancelled background task from tracking.\n\nArgs:\n params: Identifier of the completed or cancelled task to remove from tracking.\n\nReturns:\n Indicates whether the task was removed. False when the task does not exist or is still running/idle." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return TasksRemoveResult.from_dict(await self._client.request("session.tasks.remove", params_dict, **_timeout_kwargs(timeout))) + @experimental async def send_message(self, params: TasksSendMessageRequest, *, timeout: float | None = None) -> TasksSendMessageResult: "Sends a message to a background agent task.\n\nArgs:\n params: Identifier of the target agent task, message content, and optional sender agent ID.\n\nReturns:\n Indicates whether the message was delivered, with an error message when delivery failed." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} @@ -23409,30 +23499,36 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def list(self, *, timeout: float | None = None) -> SkillList: "Lists skills available to the session.\n\nReturns:\n Skills available to the session, with their enabled state." return SkillList.from_dict(await self._client.request("session.skills.list", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def get_invoked(self, *, timeout: float | None = None) -> SkillsGetInvokedResult: "Returns the skills that have been invoked during this session.\n\nReturns:\n Skills invoked during this session, ordered by invocation time (most recent last)." return SkillsGetInvokedResult.from_dict(await self._client.request("session.skills.getInvoked", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def enable(self, params: SkillsEnableRequest, *, timeout: float | None = None) -> None: "Enables a skill for the session.\n\nArgs:\n params: Name of the skill to enable for the session." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id await self._client.request("session.skills.enable", params_dict, **_timeout_kwargs(timeout)) + @experimental async def disable(self, params: SkillsDisableRequest, *, timeout: float | None = None) -> None: "Disables a skill for the session.\n\nArgs:\n params: Name of the skill to disable for the session." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id await self._client.request("session.skills.disable", params_dict, **_timeout_kwargs(timeout)) + @experimental async def reload(self, *, timeout: float | None = None) -> SkillsLoadDiagnostics: "Reloads skill definitions for the session.\n\nReturns:\n Diagnostics from reloading skill definitions, with warnings and errors as separate lists." return SkillsLoadDiagnostics.from_dict(await self._client.request("session.skills.reload", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def ensure_loaded(self, *, timeout: float | None = None) -> None: "Ensures the session's skill definitions have been loaded from disk." await self._client.request("session.skills.ensureLoaded", {"sessionId": self._session_id}, **_timeout_kwargs(timeout)) @@ -23444,6 +23540,7 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def login(self, params: MCPOauthLoginRequest, *, timeout: float | None = None) -> MCPOauthLoginResult: "Starts OAuth authentication for a remote MCP server.\n\nArgs:\n params: Remote MCP server name and optional overrides controlling reauthentication, OAuth client display name, and the callback success-page copy.\n\nReturns:\n OAuth authorization URL the caller should open, or empty when cached tokens already authenticated the server." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} @@ -23457,34 +23554,40 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def read_resource(self, params: MCPAppsReadResourceRequest, *, timeout: float | None = None) -> MCPAppsReadResourceResult: "Fetch an MCP resource (typically a `ui://` MCP App bundle, per SEP-1865) from a connected server. Requires the `mcp-apps` session capability.\n\nArgs:\n params: MCP server and resource URI to fetch.\n\nReturns:\n Resource contents returned by the MCP server." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return MCPAppsReadResourceResult.from_dict(await self._client.request("session.mcp.apps.readResource", params_dict, **_timeout_kwargs(timeout))) + @experimental async def list_tools(self, params: MCPAppsListToolsRequest, *, timeout: float | None = None) -> MCPAppsListToolsResult: "List tools that an MCP App view is allowed to call (SEP-1865 visibility filter). Returns tools whose `_meta.ui.visibility` is unset (default `[\"model\",\"app\"]`) or includes `\"app\"`.\n\nArgs:\n params: MCP server to list app-callable tools for.\n\nReturns:\n App-callable tools from the named MCP server." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return MCPAppsListToolsResult.from_dict(await self._client.request("session.mcp.apps.listTools", params_dict, **_timeout_kwargs(timeout))) + @experimental async def call_tool(self, params: MCPAppsCallToolRequest, *, timeout: float | None = None) -> dict: "Call an MCP tool from an MCP App view (SEP-1865). Enforces the visibility check that prevents an app iframe from invoking model-only tools. Returns the standard MCP `CallToolResult`.\n\nArgs:\n params: MCP server, tool name, and arguments to invoke from an MCP App view.\n\nReturns:\n Standard MCP CallToolResult" params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return dict(await self._client.request("session.mcp.apps.callTool", params_dict, **_timeout_kwargs(timeout))) + @experimental async def set_host_context(self, params: MCPAppsSetHostContextRequest, *, timeout: float | None = None) -> None: "Replace the host context returned to MCP App guests on `ui/initialize`. Hosts use this to advertise theme, locale, or other metadata to the guest UI.\n\nArgs:\n params: Host context to advertise to MCP App guests." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id await self._client.request("session.mcp.apps.setHostContext", params_dict, **_timeout_kwargs(timeout)) + @experimental async def get_host_context(self, *, timeout: float | None = None) -> MCPAppsHostContext: "Read the current host context advertised to MCP App guests.\n\nReturns:\n Current host context advertised to MCP App guests." return MCPAppsHostContext.from_dict(await self._client.request("session.mcp.apps.getHostContext", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def diagnose(self, params: MCPAppsDiagnoseRequest, *, timeout: float | None = None) -> MCPAppsDiagnoseResult: "Diagnose MCP Apps wiring for a specific MCP server. Reports the session capability, feature-flag state, advertised extension, and how many tools have `_meta.ui` populated.\n\nArgs:\n params: MCP server to diagnose MCP Apps wiring for.\n\nReturns:\n Diagnostic snapshot of MCP Apps wiring for the named server." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} @@ -23500,60 +23603,71 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self.oauth = McpOauthApi(client, session_id) self.apps = McpAppsApi(client, session_id) + @experimental async def list(self, *, timeout: float | None = None) -> MCPServerList: "Lists MCP servers configured for the session, their connection status, and host-level state. The host-level state (disabled/filtered servers, failed/needs-auth/pending connections, mcp3p policy, full config) is empty/zero when no MCP host has been initialized for the session.\n\nReturns:\n MCP servers configured for the session, with their connection status and host-level state." return MCPServerList.from_dict(await self._client.request("session.mcp.list", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def list_tools(self, params: MCPListToolsRequest, *, timeout: float | None = None) -> MCPListToolsResult: "Lists the tools exposed by a connected MCP server on this session's host.\n\nArgs:\n params: Server name whose tool list should be returned.\n\nReturns:\n Tools exposed by the connected MCP server. Throws when the server is not connected." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return MCPListToolsResult.from_dict(await self._client.request("session.mcp.listTools", params_dict, **_timeout_kwargs(timeout))) + @experimental async def enable(self, params: MCPEnableRequest, *, timeout: float | None = None) -> None: "Enables an MCP server for the session.\n\nArgs:\n params: Name of the MCP server to enable for the session." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id await self._client.request("session.mcp.enable", params_dict, **_timeout_kwargs(timeout)) + @experimental async def disable(self, params: MCPDisableRequest, *, timeout: float | None = None) -> None: "Disables an MCP server for the session.\n\nArgs:\n params: Name of the MCP server to disable for the session." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id await self._client.request("session.mcp.disable", params_dict, **_timeout_kwargs(timeout)) + @experimental async def reload(self, *, timeout: float | None = None) -> None: "Reloads MCP server connections for the session." await self._client.request("session.mcp.reload", {"sessionId": self._session_id}, **_timeout_kwargs(timeout)) + @experimental async def execute_sampling(self, params: MCPExecuteSamplingParams, *, timeout: float | None = None) -> MCPSamplingExecutionResult: "Runs an MCP sampling inference on behalf of an MCP server.\n\nArgs:\n params: Identifiers and raw MCP CreateMessageRequest params used to run a sampling inference.\n\nReturns:\n Outcome of an MCP sampling execution: success result, failure error, or cancellation." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return MCPSamplingExecutionResult.from_dict(await self._client.request("session.mcp.executeSampling", params_dict, **_timeout_kwargs(timeout))) + @experimental async def cancel_sampling_execution(self, params: MCPCancelSamplingExecutionParams, *, timeout: float | None = None) -> MCPCancelSamplingExecutionResult: "Cancels an in-flight MCP sampling execution by request ID.\n\nArgs:\n params: The requestId previously passed to executeSampling that should be cancelled.\n\nReturns:\n Indicates whether an in-flight sampling execution with the given requestId was found and cancelled." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return MCPCancelSamplingExecutionResult.from_dict(await self._client.request("session.mcp.cancelSamplingExecution", params_dict, **_timeout_kwargs(timeout))) + @experimental async def set_env_value_mode(self, params: MCPSetEnvValueModeParams, *, timeout: float | None = None) -> MCPSetEnvValueModeResult: "Sets how environment-variable values supplied to MCP servers are resolved (direct or indirect).\n\nArgs:\n params: Mode controlling how MCP server env values are resolved (`direct` or `indirect`).\n\nReturns:\n Env-value mode recorded on the session after the update." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return MCPSetEnvValueModeResult.from_dict(await self._client.request("session.mcp.setEnvValueMode", params_dict, **_timeout_kwargs(timeout))) + @experimental async def remove_git_hub(self, *, timeout: float | None = None) -> MCPRemoveGitHubResult: "Removes the auto-managed `github` MCP server when present.\n\nReturns:\n Indicates whether the auto-managed `github` MCP server was removed (false when nothing to remove)." return MCPRemoveGitHubResult.from_dict(await self._client.request("session.mcp.removeGitHub", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def stop_server(self, params: MCPStopServerRequest, *, timeout: float | None = None) -> None: "Stops an individual MCP server on the session's host.\n\nArgs:\n params: Server name for an individual MCP server stop." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id await self._client.request("session.mcp.stopServer", params_dict, **_timeout_kwargs(timeout)) + @experimental async def is_server_running(self, params: MCPIsServerRunningRequest, *, timeout: float | None = None) -> MCPIsServerRunningResult: "Checks whether a named MCP server is currently running on the session's host.\n\nArgs:\n params: Server name to check running status for.\n\nReturns:\n Whether the named MCP server is running." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} @@ -23567,10 +23681,12 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def list(self, *, timeout: float | None = None) -> PluginList: "Lists plugins installed for the session.\n\nReturns:\n Plugins installed for the session, with their enabled state and version metadata." return PluginList.from_dict(await self._client.request("session.plugins.list", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def reload(self, params: PluginsReloadRequest | None = None, *, timeout: float | None = None) -> None: "Reloads the session's plugin set, refreshing MCP servers, custom agents, hooks, and skills cache so SDK-driven changes via `server.plugins.*` take effect immediately.\n\nArgs:\n params: Optional flags controlling which side effects the reload performs." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} if params is not None else {} @@ -23584,6 +23700,7 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def get_endpoint(self, params: ProviderGetEndpointRequest | None = None, *, timeout: float | None = None) -> ProviderEndpoint: "Returns the provider endpoint and credentials the session is currently configured to talk to, so the caller can make inference calls directly against the same backend the session uses.\n\nArgs:\n params: Optional model identifier to scope the endpoint snapshot to.\n\nReturns:\n A snapshot of the provider endpoint the session is currently configured to talk to." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} if params is not None else {} @@ -23597,6 +23714,7 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def update(self, params: SessionUpdateOptionsParams, *, timeout: float | None = None) -> SessionUpdateOptionsResult: "Patches the genuinely-mutable subset of session options.\n\nArgs:\n params: Patch of mutable session options to apply to the running session.\n\nReturns:\n Indicates whether the session options patch was applied successfully." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} @@ -23610,6 +23728,7 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def initialize(self, params: LspInitializeRequest, *, timeout: float | None = None) -> None: "Loads the merged LSP configuration set for the session's working directory.\n\nArgs:\n params: Parameters for (re)loading the merged LSP configuration set." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} @@ -23623,26 +23742,31 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def list(self, *, timeout: float | None = None) -> ExtensionList: "Lists extensions discovered for the session and their current status.\n\nReturns:\n Extensions discovered for the session, with their current status." return ExtensionList.from_dict(await self._client.request("session.extensions.list", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def enable(self, params: ExtensionsEnableRequest, *, timeout: float | None = None) -> None: "Enables an extension for the session.\n\nArgs:\n params: Source-qualified extension identifier to enable for the session." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id await self._client.request("session.extensions.enable", params_dict, **_timeout_kwargs(timeout)) + @experimental async def disable(self, params: ExtensionsDisableRequest, *, timeout: float | None = None) -> None: "Disables an extension for the session.\n\nArgs:\n params: Source-qualified extension identifier to disable for the session." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id await self._client.request("session.extensions.disable", params_dict, **_timeout_kwargs(timeout)) + @experimental async def reload(self, *, timeout: float | None = None) -> None: "Reloads extension definitions and processes for the session." await self._client.request("session.extensions.reload", {"sessionId": self._session_id}, **_timeout_kwargs(timeout)) + @experimental async def send_attachments_to_message(self, params: SendAttachmentsToMessageParams, *, timeout: float | None = None) -> None: "Push attachments into the next user-message turn from an extension. The host should surface them as composer pills and forward them via the next session.send call. Callable only by extension-owned connections.\n\nArgs:\n params: Parameters for session.extensions.sendAttachmentsToMessage." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} @@ -23656,20 +23780,24 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def handle_pending_tool_call(self, params: HandlePendingToolCallRequest, *, timeout: float | None = None) -> HandlePendingToolCallResult: "Provides the result for a pending external tool call.\n\nArgs:\n params: Pending external tool call request ID, with the tool result or an error describing why it failed.\n\nReturns:\n Indicates whether the external tool call result was handled successfully." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return HandlePendingToolCallResult.from_dict(await self._client.request("session.tools.handlePendingToolCall", params_dict, **_timeout_kwargs(timeout))) + @experimental async def initialize_and_validate(self, *, timeout: float | None = None) -> ToolsInitializeAndValidateResult: "Resolves, builds, and validates the runtime tool list for the session.\n\nReturns:\n Resolve, build, and validate the runtime tool list for this session. Subagent sessions and consumer flows that need an initialized tool set before `send` invoke this. Default base-class implementation is a no-op for sessions that don't support tool validation." return ToolsInitializeAndValidateResult.from_dict(await self._client.request("session.tools.initializeAndValidate", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def get_current_metadata(self, *, timeout: float | None = None) -> ToolsGetCurrentMetadataResult: "Returns lightweight metadata for the session's currently initialized tools.\n\nReturns:\n Current lightweight tool metadata snapshot for the session." return ToolsGetCurrentMetadataResult.from_dict(await self._client.request("session.tools.getCurrentMetadata", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def update_subagent_settings(self, params: UpdateSubagentSettingsRequest, *, timeout: float | None = None) -> ToolsUpdateSubagentSettingsResult: "Updates the current session's live subagent settings after user settings change. The persisted user settings remain the source of truth for future sessions.\n\nArgs:\n params: Subagent settings to apply to the current session\n\nReturns:\n Empty result after applying subagent settings" params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} @@ -23683,36 +23811,42 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def list(self, params: CommandsListRequest | None = None, *, timeout: float | None = None) -> CommandList: "Lists slash commands available in the session.\n\nArgs:\n params: Optional filters controlling which command sources to include in the listing.\n\nReturns:\n Slash commands available in the session, after applying any include/exclude filters." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} if params is not None else {} params_dict["sessionId"] = self._session_id return CommandList.from_dict(await self._client.request("session.commands.list", params_dict, **_timeout_kwargs(timeout))) + @experimental async def invoke(self, params: CommandsInvokeRequest, *, timeout: float | None = None) -> SlashCommandInvocationResult: "Invokes a slash command in the session.\n\nArgs:\n params: Slash command name and optional raw input string to invoke.\n\nReturns:\n Result of invoking the slash command (text output, prompt to send to the agent, or completion)." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return _load_SlashCommandInvocationResult(await self._client.request("session.commands.invoke", params_dict, **_timeout_kwargs(timeout))) + @experimental async def handle_pending_command(self, params: CommandsHandlePendingCommandRequest, *, timeout: float | None = None) -> CommandsHandlePendingCommandResult: "Reports completion of a pending client-handled slash command.\n\nArgs:\n params: Pending command request ID and an optional error if the client handler failed.\n\nReturns:\n Indicates whether the pending client-handled command was completed successfully." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return CommandsHandlePendingCommandResult.from_dict(await self._client.request("session.commands.handlePendingCommand", params_dict, **_timeout_kwargs(timeout))) + @experimental async def execute(self, params: ExecuteCommandParams, *, timeout: float | None = None) -> ExecuteCommandResult: "Executes a slash command synchronously and returns any error.\n\nArgs:\n params: Slash command name and argument string to execute synchronously.\n\nReturns:\n Error message produced while executing the command, if any." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return ExecuteCommandResult.from_dict(await self._client.request("session.commands.execute", params_dict, **_timeout_kwargs(timeout))) + @experimental async def enqueue(self, params: EnqueueCommandParams, *, timeout: float | None = None) -> EnqueueCommandResult: "Enqueues a slash command for FIFO processing on the local session.\n\nArgs:\n params: Slash-prefixed command string to enqueue for FIFO processing.\n\nReturns:\n Indicates whether the command was accepted into the local execution queue." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return EnqueueCommandResult.from_dict(await self._client.request("session.commands.enqueue", params_dict, **_timeout_kwargs(timeout))) + @experimental async def respond_to_queued_command(self, params: CommandsRespondToQueuedCommandRequest, *, timeout: float | None = None) -> CommandsRespondToQueuedCommandResult: "Reports whether the host actually executed a queued command and whether to continue processing.\n\nArgs:\n params: Queued-command request ID and the result indicating whether the host executed it (and whether to stop processing further queued commands).\n\nReturns:\n Indicates whether the queued-command response was matched to a pending request." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} @@ -23726,10 +23860,12 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def get_engagement_id(self, *, timeout: float | None = None) -> SessionTelemetryEngagement: "Gets the telemetry engagement ID currently associated with the session, when available.\n\nReturns:\n Telemetry engagement ID for the session, when available." return SessionTelemetryEngagement.from_dict(await self._client.request("session.telemetry.getEngagementId", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def set_feature_overrides(self, params: TelemetrySetFeatureOverridesRequest, *, timeout: float | None = None) -> None: "Sets feature override key/value pairs to attach to subsequent telemetry events for the session.\n\nArgs:\n params: Feature override key/value pairs to attach to subsequent telemetry events from this session." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} @@ -23743,52 +23879,61 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def ephemeral_query(self, params: UIEphemeralQueryRequest, *, timeout: float | None = None) -> UIEphemeralQueryResult: "Runs a transient no-tools model query against the current conversation context.\n\nArgs:\n params: Transient question to answer without adding it to conversation history.\n\nReturns:\n Transient answer generated from current conversation context." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return UIEphemeralQueryResult.from_dict(await self._client.request("session.ui.ephemeralQuery", params_dict, **_timeout_kwargs(timeout))) + @experimental async def elicitation(self, params: UIElicitationRequest, *, timeout: float | None = None) -> UIElicitationResponse: "Requests structured input from a UI-capable client.\n\nArgs:\n params: Prompt message and JSON schema describing the form fields to elicit from the user.\n\nReturns:\n The elicitation response (accept with form values, decline, or cancel)" params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return UIElicitationResponse.from_dict(await self._client.request("session.ui.elicitation", params_dict, **_timeout_kwargs(timeout))) + @experimental async def handle_pending_elicitation(self, params: UIHandlePendingElicitationRequest, *, timeout: float | None = None) -> UIElicitationResult: "Provides the user response for a pending elicitation request.\n\nArgs:\n params: Pending elicitation request ID and the user's response (accept/decline/cancel + form values).\n\nReturns:\n Indicates whether the elicitation response was accepted; false if it was already resolved by another client." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return UIElicitationResult.from_dict(await self._client.request("session.ui.handlePendingElicitation", params_dict, **_timeout_kwargs(timeout))) + @experimental async def handle_pending_user_input(self, params: UIHandlePendingUserInputRequest, *, timeout: float | None = None) -> UIHandlePendingResult: "Resolves a pending `user_input.requested` event with the user's response.\n\nArgs:\n params: Request ID of a pending `user_input.requested` event and the user's response.\n\nReturns:\n Indicates whether the pending UI request was resolved by this call." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return UIHandlePendingResult.from_dict(await self._client.request("session.ui.handlePendingUserInput", params_dict, **_timeout_kwargs(timeout))) + @experimental async def handle_pending_sampling(self, params: UIHandlePendingSamplingRequest, *, timeout: float | None = None) -> UIHandlePendingResult: "Resolves a pending `sampling.requested` event with a sampling result, or rejects it.\n\nArgs:\n params: Request ID of a pending `sampling.requested` event and an optional sampling result payload (omit to reject).\n\nReturns:\n Indicates whether the pending UI request was resolved by this call." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return UIHandlePendingResult.from_dict(await self._client.request("session.ui.handlePendingSampling", params_dict, **_timeout_kwargs(timeout))) + @experimental async def handle_pending_auto_mode_switch(self, params: UIHandlePendingAutoModeSwitchRequest, *, timeout: float | None = None) -> UIHandlePendingResult: "Resolves a pending `auto_mode_switch.requested` event with the user's accept/decline decision.\n\nArgs:\n params: Request ID of a pending `auto_mode_switch.requested` event and the user's response.\n\nReturns:\n Indicates whether the pending UI request was resolved by this call." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return UIHandlePendingResult.from_dict(await self._client.request("session.ui.handlePendingAutoModeSwitch", params_dict, **_timeout_kwargs(timeout))) + @experimental async def handle_pending_exit_plan_mode(self, params: UIHandlePendingExitPlanModeRequest, *, timeout: float | None = None) -> UIHandlePendingResult: "Resolves a pending `exit_plan_mode.requested` event with the user's response.\n\nArgs:\n params: Request ID of a pending `exit_plan_mode.requested` event and the user's response.\n\nReturns:\n Indicates whether the pending UI request was resolved by this call." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return UIHandlePendingResult.from_dict(await self._client.request("session.ui.handlePendingExitPlanMode", params_dict, **_timeout_kwargs(timeout))) + @experimental async def register_direct_auto_mode_switch_handler(self, *, timeout: float | None = None) -> UIRegisterDirectAutoModeSwitchHandlerResult: "Registers an in-process handler for auto-mode-switch requests so the server bridge skips dispatch.\n\nReturns:\n Register an in-process handler for `auto_mode_switch.requested` events. The caller still attaches the actual listener via the standard event-subscription mechanism; this registration solely tells the server bridge to skip its own dispatch (so a remote client doesn't race the in-process handler for the same requestId)." return UIRegisterDirectAutoModeSwitchHandlerResult.from_dict(await self._client.request("session.ui.registerDirectAutoModeSwitchHandler", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def unregister_direct_auto_mode_switch_handler(self, params: UIUnregisterDirectAutoModeSwitchHandlerRequest, *, timeout: float | None = None) -> UIUnregisterDirectAutoModeSwitchHandlerResult: "Unregisters a previously-registered in-process auto-mode-switch handler by its opaque handle.\n\nArgs:\n params: Opaque handle previously returned by `registerDirectAutoModeSwitchHandler` to release.\n\nReturns:\n Indicates whether the handle was active and the registration count was decremented." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} @@ -23802,28 +23947,33 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def list(self, *, timeout: float | None = None) -> PermissionPathsList: "Returns the session's allowed directories and primary working directory.\n\nReturns:\n Snapshot of the session's allow-listed directories and primary working directory." return PermissionPathsList.from_dict(await self._client.request("session.permissions.paths.list", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def add(self, params: PermissionPathsAddParams, *, timeout: float | None = None) -> PermissionsPathsAddResult: "Adds a directory to the session's allow-list.\n\nArgs:\n params: Directory path to add to the session's allowed directories.\n\nReturns:\n Indicates whether the operation succeeded." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return PermissionsPathsAddResult.from_dict(await self._client.request("session.permissions.paths.add", params_dict, **_timeout_kwargs(timeout))) + @experimental async def update_primary(self, params: PermissionPathsUpdatePrimaryParams, *, timeout: float | None = None) -> PermissionsPathsUpdatePrimaryResult: "Updates the session's primary working directory used by the permission policy.\n\nArgs:\n params: Directory path to set as the session's new primary working directory.\n\nReturns:\n Indicates whether the operation succeeded." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return PermissionsPathsUpdatePrimaryResult.from_dict(await self._client.request("session.permissions.paths.updatePrimary", params_dict, **_timeout_kwargs(timeout))) + @experimental async def is_path_within_allowed_directories(self, params: PermissionPathsAllowedCheckParams, *, timeout: float | None = None) -> PermissionPathsAllowedCheckResult: "Reports whether a path falls within any of the session's allowed directories.\n\nArgs:\n params: Path to evaluate against the session's allowed directories.\n\nReturns:\n Indicates whether the supplied path is within the session's allowed directories." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return PermissionPathsAllowedCheckResult.from_dict(await self._client.request("session.permissions.paths.isPathWithinAllowedDirectories", params_dict, **_timeout_kwargs(timeout))) + @experimental async def is_path_within_workspace(self, params: PermissionPathsWorkspaceCheckParams, *, timeout: float | None = None) -> PermissionPathsWorkspaceCheckResult: "Reports whether a path falls within the session's workspace (primary) directory.\n\nArgs:\n params: Path to evaluate against the session's workspace (primary) directory.\n\nReturns:\n Indicates whether the supplied path is within the session's workspace directory." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} @@ -23837,18 +23987,21 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def resolve(self, params: PermissionLocationResolveParams, *, timeout: float | None = None) -> PermissionLocationResolveResult: "Resolves the permission location key and type for a working directory.\n\nArgs:\n params: Working directory to resolve into a location-permissions key.\n\nReturns:\n Resolved location-permissions key and type." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return PermissionLocationResolveResult.from_dict(await self._client.request("session.permissions.locations.resolve", params_dict, **_timeout_kwargs(timeout))) + @experimental async def apply(self, params: PermissionLocationApplyParams, *, timeout: float | None = None) -> PermissionLocationApplyResult: "Applies persisted location-scoped tool approvals and allowed directories for a working directory to this session's permission service.\n\nArgs:\n params: Working directory to load persisted location permissions for.\n\nReturns:\n Summary of persisted location permissions applied to the session." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return PermissionLocationApplyResult.from_dict(await self._client.request("session.permissions.locations.apply", params_dict, **_timeout_kwargs(timeout))) + @experimental async def add_tool_approval(self, params: PermissionLocationAddToolApprovalParams, *, timeout: float | None = None) -> PermissionsLocationsAddToolApprovalResult: "Persists a tool approval for a permission location and applies its rules to this session's live permission service.\n\nArgs:\n params: Location-scoped tool approval to persist.\n\nReturns:\n Indicates whether the operation succeeded." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} @@ -23862,12 +24015,14 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def is_trusted(self, params: FolderTrustCheckParams, *, timeout: float | None = None) -> FolderTrustCheckResult: "Reports whether a folder is trusted according to the user's folder trust state.\n\nArgs:\n params: Folder path to check for trust.\n\nReturns:\n Folder trust check result." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return FolderTrustCheckResult.from_dict(await self._client.request("session.permissions.folderTrust.isTrusted", params_dict, **_timeout_kwargs(timeout))) + @experimental async def add_trusted(self, params: FolderTrustAddParams, *, timeout: float | None = None) -> PermissionsFolderTrustAddTrustedResult: "Adds a folder to the user's trusted folders list.\n\nArgs:\n params: Folder path to add to trusted folders.\n\nReturns:\n Indicates whether the operation succeeded." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} @@ -23881,6 +24036,7 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def set_unrestricted_mode(self, params: PermissionUrlsSetUnrestrictedModeParams, *, timeout: float | None = None) -> PermissionsUrlsSetUnrestrictedModeResult: "Toggles the runtime's URL-permission policy between unrestricted and restricted modes.\n\nArgs:\n params: Whether the URL-permission policy should run in unrestricted mode.\n\nReturns:\n Indicates whether the operation succeeded." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} @@ -23898,54 +24054,64 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self.folder_trust = PermissionsFolderTrustApi(client, session_id) self.urls = PermissionsUrlsApi(client, session_id) + @experimental async def configure(self, params: PermissionsConfigureParams, *, timeout: float | None = None) -> PermissionsConfigureResult: "Replaces selected permission policy fields (rules, paths, URLs, exclusions, allow-all flags) on the session.\n\nArgs:\n params: Patch of permission policy fields to apply (omit a field to leave it unchanged).\n\nReturns:\n Indicates whether the operation succeeded." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return PermissionsConfigureResult.from_dict(await self._client.request("session.permissions.configure", params_dict, **_timeout_kwargs(timeout))) + @experimental async def handle_pending_permission_request(self, params: PermissionDecisionRequest, *, timeout: float | None = None) -> PermissionRequestResult: "Provides a decision for a pending tool permission request.\n\nArgs:\n params: Pending permission request ID and the decision to apply (approve/reject and scope).\n\nReturns:\n Indicates whether the permission decision was applied; false when the request was already resolved." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return PermissionRequestResult.from_dict(await self._client.request("session.permissions.handlePendingPermissionRequest", params_dict, **_timeout_kwargs(timeout))) + @experimental async def pending_requests(self, *, timeout: float | None = None) -> PendingPermissionRequestList: "Reconstructs the set of pending tool permission requests from the session's event history.\n\nReturns:\n List of pending permission requests reconstructed from event history." return PendingPermissionRequestList.from_dict(await self._client.request("session.permissions.pendingRequests", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def set_approve_all(self, params: PermissionsSetApproveAllRequest, *, timeout: float | None = None) -> PermissionsSetApproveAllResult: "Enables or disables automatic approval of tool permission requests for the session.\n\nArgs:\n params: Allow-all toggle for tool permission requests, with an optional telemetry source.\n\nReturns:\n Indicates whether the operation succeeded." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return PermissionsSetApproveAllResult.from_dict(await self._client.request("session.permissions.setApproveAll", params_dict, **_timeout_kwargs(timeout))) + @experimental async def set_allow_all(self, params: PermissionsSetAllowAllRequest, *, timeout: float | None = None) -> AllowAllPermissionSetResult: "Enables or disables full allow-all permissions (tools, paths, and URLs) for the session. Used by attach-mode clients (e.g. LocalRpcSession's `/allow-all` forwarder) to flip the target session's permission state. Unlike `setApproveAll`, this swaps in the unrestricted path and URL managers and emits `session.permissions_changed` on transition. The result returns the authoritative post-mutation state so callers can update their local mirrors without racing the `session.permissions_changed` notification on the same wire.\n\nArgs:\n params: Whether to enable full allow-all permissions for the session.\n\nReturns:\n Indicates whether the operation succeeded and reports the post-mutation state." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return AllowAllPermissionSetResult.from_dict(await self._client.request("session.permissions.setAllowAll", params_dict, **_timeout_kwargs(timeout))) + @experimental async def get_allow_all(self, *, timeout: float | None = None) -> AllowAllPermissionState: "Returns whether full allow-all permissions are currently active for the session.\n\nReturns:\n Current full allow-all permission state." return AllowAllPermissionState.from_dict(await self._client.request("session.permissions.getAllowAll", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def modify_rules(self, params: PermissionsModifyRulesParams, *, timeout: float | None = None) -> PermissionsModifyRulesResult: "Adds or removes session-scoped or location-scoped permission rules.\n\nArgs:\n params: Scope and add/remove instructions for modifying session- or location-scoped permission rules.\n\nReturns:\n Indicates whether the operation succeeded." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return PermissionsModifyRulesResult.from_dict(await self._client.request("session.permissions.modifyRules", params_dict, **_timeout_kwargs(timeout))) + @experimental async def set_required(self, params: PermissionsSetRequiredRequest, *, timeout: float | None = None) -> PermissionsSetRequiredResult: "Sets whether the client wants permission prompts bridged into session events.\n\nArgs:\n params: Toggles whether permission prompts should be bridged into session events for this client.\n\nReturns:\n Indicates whether the operation succeeded." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return PermissionsSetRequiredResult.from_dict(await self._client.request("session.permissions.setRequired", params_dict, **_timeout_kwargs(timeout))) + @experimental async def reset_session_approvals(self, *, timeout: float | None = None) -> PermissionsResetSessionApprovalsResult: "Clears session-scoped tool permission approvals.\n\nReturns:\n Indicates whether the operation succeeded." return PermissionsResetSessionApprovalsResult.from_dict(await self._client.request("session.permissions.resetSessionApprovals", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def notify_prompt_shown(self, params: PermissionPromptShownNotification, *, timeout: float | None = None) -> PermissionsNotifyPromptShownResult: "Notifies the runtime that a permission prompt UI has been shown to the user.\n\nArgs:\n params: Notification payload describing the permission prompt that the client just rendered.\n\nReturns:\n Indicates whether the operation succeeded." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} @@ -23959,36 +24125,43 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def snapshot(self, *, timeout: float | None = None) -> SessionMetadataSnapshot: "Returns a snapshot of the session's identifying metadata, mode, agent, and remote info.\n\nReturns:\n Point-in-time snapshot of slow-changing session identifier and state fields" return SessionMetadataSnapshot.from_dict(await self._client.request("session.metadata.snapshot", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def is_processing(self, *, timeout: float | None = None) -> MetadataIsProcessingResult: "Reports whether the local session is currently processing user/agent messages.\n\nReturns:\n Indicates whether the local session is currently processing a turn or background continuation." return MetadataIsProcessingResult.from_dict(await self._client.request("session.metadata.isProcessing", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def activity(self, *, timeout: float | None = None) -> SessionActivity: "Returns a snapshot of activity flags for the session.\n\nReturns:\n Current activity flags for the session." return SessionActivity.from_dict(await self._client.request("session.metadata.activity", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def context_info(self, params: MetadataContextInfoRequest, *, timeout: float | None = None) -> MetadataContextInfoResult: "Returns the token breakdown for the session's current context window for a given model.\n\nArgs:\n params: Model identifier and token limits used to compute the context-info breakdown.\n\nReturns:\n Token breakdown for the session's current context window, or null if uninitialized." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return MetadataContextInfoResult.from_dict(await self._client.request("session.metadata.contextInfo", params_dict, **_timeout_kwargs(timeout))) + @experimental async def record_context_change(self, params: MetadataRecordContextChangeRequest, *, timeout: float | None = None) -> MetadataRecordContextChangeResult: "Records a working-directory/git context change and emits a `session.context_changed` event.\n\nArgs:\n params: Updated working-directory/git context to record on the session.\n\nReturns:\n Notify the session that its working directory context has changed. Emits a `session.context_changed` event so consumers (telemetry, OTel tracker, ACP, the timeline UI) can react. Use this when the host has detected a cwd/branch/repo change outside the session's normal lifecycle (e.g., after a shell command in interactive mode)." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return MetadataRecordContextChangeResult.from_dict(await self._client.request("session.metadata.recordContextChange", params_dict, **_timeout_kwargs(timeout))) + @experimental async def set_working_directory(self, params: MetadataSetWorkingDirectoryRequest, *, timeout: float | None = None) -> MetadataSetWorkingDirectoryResult: "Updates the session's recorded working directory.\n\nArgs:\n params: Absolute path to set as the session's new working directory.\n\nReturns:\n Update the session's working directory. Used by the host when the user explicitly changes cwd (e.g., the `/cd` slash command). The host is responsible for `process.chdir` and any related side-effects (file index, etc.); this method only updates the session's own recorded path." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return MetadataSetWorkingDirectoryResult.from_dict(await self._client.request("session.metadata.setWorkingDirectory", params_dict, **_timeout_kwargs(timeout))) + @experimental async def recompute_context_tokens(self, params: MetadataRecomputeContextTokensRequest, *, timeout: float | None = None) -> MetadataRecomputeContextTokensResult: "Re-tokenizes the session's existing messages against a model and returns aggregate token totals.\n\nArgs:\n params: Model identifier to use when re-tokenizing the session's existing messages.\n\nReturns:\n Re-tokenize the session's existing messages against `modelId` and return the token totals. Useful for hosts that want an initial estimate of context usage on session resume, before the next agent turn fires `session.context_info_changed` events. Returns zeros for an empty session." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} @@ -24002,24 +24175,28 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def exec(self, params: ShellExecRequest, *, timeout: float | None = None) -> ShellExecResult: "Starts a shell command and streams output through session notifications.\n\nArgs:\n params: Shell command to run, with optional working directory and timeout in milliseconds.\n\nReturns:\n Identifier of the spawned process, used to correlate streamed output and exit notifications." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return ShellExecResult.from_dict(await self._client.request("session.shell.exec", params_dict, **_timeout_kwargs(timeout))) + @experimental async def kill(self, params: ShellKillRequest, *, timeout: float | None = None) -> ShellKillResult: "Sends a signal to a shell process previously started via \"shell.exec\".\n\nArgs:\n params: Identifier of a process previously returned by \"shell.exec\" and the signal to send.\n\nReturns:\n Indicates whether the signal was delivered; false if the process was unknown or already exited." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return ShellKillResult.from_dict(await self._client.request("session.shell.kill", params_dict, **_timeout_kwargs(timeout))) + @experimental async def execute_user_requested(self, params: ShellExecuteUserRequestedRequest, *, timeout: float | None = None) -> UserRequestedShellCommandResult: "Executes a user-requested shell command through the session runtime.\n\nArgs:\n params: User-requested shell command and cancellation handle.\n\nReturns:\n Result of a user-requested shell command." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return UserRequestedShellCommandResult.from_dict(await self._client.request("session.shell.executeUserRequested", params_dict, **_timeout_kwargs(timeout))) + @experimental async def cancel_user_requested(self, params: ShellCancelUserRequestedRequest, *, timeout: float | None = None) -> CancelUserRequestedShellCommandResult: "Cancels a user-requested shell command by request ID.\n\nArgs:\n params: User-requested shell execution cancellation handle.\n\nReturns:\n Cancellation result for a user-requested shell command." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} @@ -24033,26 +24210,31 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def compact(self, params: HistoryCompactRequest | None = None, *, timeout: float | None = None) -> HistoryCompactResult: "Compacts the session history to reduce context usage.\n\nArgs:\n params: Optional compaction parameters.\n\nReturns:\n Compaction outcome with the number of tokens and messages removed, summary text, and the resulting context window breakdown." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} if params is not None else {} params_dict["sessionId"] = self._session_id return HistoryCompactResult.from_dict(await self._client.request("session.history.compact", params_dict, **_timeout_kwargs(timeout))) + @experimental async def truncate(self, params: HistoryTruncateRequest, *, timeout: float | None = None) -> HistoryTruncateResult: "Truncates persisted session history to a specific event.\n\nArgs:\n params: Identifier of the event to truncate to; this event and all later events are removed.\n\nReturns:\n Number of events that were removed by the truncation." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return HistoryTruncateResult.from_dict(await self._client.request("session.history.truncate", params_dict, **_timeout_kwargs(timeout))) + @experimental async def cancel_background_compaction(self, *, timeout: float | None = None) -> HistoryCancelBackgroundCompactionResult: "Cancels any in-progress background compaction on a local session.\n\nReturns:\n Indicates whether an in-progress background compaction was cancelled." return HistoryCancelBackgroundCompactionResult.from_dict(await self._client.request("session.history.cancelBackgroundCompaction", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def abort_manual_compaction(self, *, timeout: float | None = None) -> HistoryAbortManualCompactionResult: "Aborts any in-progress manual compaction on a local session.\n\nReturns:\n Indicates whether an in-progress manual compaction was aborted." return HistoryAbortManualCompactionResult.from_dict(await self._client.request("session.history.abortManualCompaction", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def summarize_for_handoff(self, *, timeout: float | None = None) -> HistorySummarizeForHandoffResult: "Produces a markdown summary of the session's conversation context for hand-off scenarios.\n\nReturns:\n Markdown summary of the conversation context (empty when not available)." return HistorySummarizeForHandoffResult.from_dict(await self._client.request("session.history.summarizeForHandoff", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) @@ -24064,14 +24246,17 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def pending_items(self, *, timeout: float | None = None) -> QueuePendingItemsResult: "Returns the local session's pending user-facing queued items and steering messages.\n\nReturns:\n Snapshot of the session's pending queued items and immediate-steering messages." return QueuePendingItemsResult.from_dict(await self._client.request("session.queue.pendingItems", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def remove_most_recent(self, *, timeout: float | None = None) -> QueueRemoveMostRecentResult: "Removes the most recently queued user-facing item (LIFO).\n\nReturns:\n Indicates whether a user-facing pending item was removed." return QueueRemoveMostRecentResult.from_dict(await self._client.request("session.queue.removeMostRecent", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def clear(self, *, timeout: float | None = None) -> None: "Clears all pending queued items on the local session." await self._client.request("session.queue.clear", {"sessionId": self._session_id}, **_timeout_kwargs(timeout)) @@ -24083,22 +24268,26 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def read(self, params: EventLogReadRequest, *, timeout: float | None = None) -> EventsReadResult: "Reads a batch of session events from a cursor, optionally waiting for new events.\n\nArgs:\n params: Cursor, batch size, and optional long-poll/filter parameters for reading session events.\n\nReturns:\n Batch of session events returned by a read, with cursor and continuation metadata." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return EventsReadResult.from_dict(await self._client.request("session.eventLog.read", params_dict, **_timeout_kwargs(timeout))) + @experimental async def tail(self, *, timeout: float | None = None) -> EventLogTailResult: "Returns a snapshot of the current tail cursor without consuming events.\n\nReturns:\n Snapshot of the current tail cursor without returning any events. Use this when a consumer wants to subscribe to live events going forward without first paginating through the entire persisted history (which would happen if `read` were called without a cursor on a long-lived session)." return EventLogTailResult.from_dict(await self._client.request("session.eventLog.tail", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def register_interest(self, params: RegisterEventInterestParams, *, timeout: float | None = None) -> RegisterEventInterestResult: "Registers consumer interest in an event type for runtime gating purposes.\n\nArgs:\n params: Event type to register consumer interest for, used by runtime gating logic.\n\nReturns:\n Opaque handle representing an event-type interest registration." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return RegisterEventInterestResult.from_dict(await self._client.request("session.eventLog.registerInterest", params_dict, **_timeout_kwargs(timeout))) + @experimental async def release_interest(self, params: ReleaseEventInterestParams, *, timeout: float | None = None) -> EventLogReleaseInterestResult: "Releases a consumer's previously-registered interest in an event type.\n\nArgs:\n params: Opaque handle previously returned by `registerInterest` to release.\n\nReturns:\n Indicates whether the operation succeeded." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} @@ -24112,6 +24301,7 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def get_metrics(self, *, timeout: float | None = None) -> UsageGetMetricsResult: "Gets accumulated usage metrics for the session.\n\nReturns:\n Accumulated session usage metrics, including premium request cost, token counts, model breakdown, and code-change totals." return UsageGetMetricsResult.from_dict(await self._client.request("session.usage.getMetrics", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) @@ -24123,16 +24313,19 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def enable(self, params: RemoteEnableRequest, *, timeout: float | None = None) -> RemoteEnableResult: "Enables remote session export or steering.\n\nArgs:\n params: Optional remote session mode (\"off\", \"export\", or \"on\"); defaults to enabling both export and remote steering.\n\nReturns:\n GitHub URL for the session and a flag indicating whether remote steering is enabled." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return RemoteEnableResult.from_dict(await self._client.request("session.remote.enable", params_dict, **_timeout_kwargs(timeout))) + @experimental async def disable(self, *, timeout: float | None = None) -> None: "Disables remote session export and steering." await self._client.request("session.remote.disable", {"sessionId": self._session_id}, **_timeout_kwargs(timeout)) + @experimental async def notify_steerable_changed(self, params: RemoteNotifySteerableChangedRequest, *, timeout: float | None = None) -> RemoteNotifySteerableChangedResult: "Persists a remote-steerability change emitted by the host as a session event.\n\nArgs:\n params: New remote-steerability state to persist as a `session.remote_steerable_changed` event.\n\nReturns:\n Persist a steerability change as a `session.remote_steerable_changed` event. Used by the host (CLI / SDK consumer) when it has just finished enabling or disabling steering on a remote exporter that the runtime does not directly own." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} @@ -24146,10 +24339,12 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id + @experimental async def list(self, *, timeout: float | None = None) -> ScheduleList: "Lists the session's currently active scheduled prompts.\n\nReturns:\n Snapshot of the currently active recurring prompts for this session." return ScheduleList.from_dict(await self._client.request("session.schedule.list", {"sessionId": self._session_id}, **_timeout_kwargs(timeout))) + @experimental async def stop(self, params: ScheduleStopRequest, *, timeout: float | None = None) -> ScheduleStopResult: "Removes a scheduled prompt by id.\n\nArgs:\n params: Identifier of the scheduled prompt to remove.\n\nReturns:\n Remove a scheduled prompt by id. The result entry is omitted if the id was unknown." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} @@ -24194,28 +24389,33 @@ def __init__(self, client: "JsonRpcClient", session_id: str): self.remote = RemoteApi(client, session_id) self.schedule = ScheduleApi(client, session_id) + @experimental async def suspend(self, *, timeout: float | None = None) -> None: "Suspends the session while preserving persisted state for later resume.\n\n.. warning:: This API is experimental and may change or be removed in future versions." await self._client.request("session.suspend", {"sessionId": self._session_id}, **_timeout_kwargs(timeout)) + @experimental async def send(self, params: SendRequest, *, timeout: float | None = None) -> SendResult: "Sends a user message to the session and returns its message ID.\n\nArgs:\n params: Parameters for sending a user message to the session\n\nReturns:\n Result of sending a user message\n\n.. warning:: This API is experimental and may change or be removed in future versions." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return SendResult.from_dict(await self._client.request("session.send", params_dict, **_timeout_kwargs(timeout))) + @experimental async def abort(self, params: AbortRequest, *, timeout: float | None = None) -> AbortResult: "Aborts the current agent turn.\n\nArgs:\n params: Parameters for aborting the current turn\n\nReturns:\n Result of aborting the current turn\n\n.. warning:: This API is experimental and may change or be removed in future versions." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id return AbortResult.from_dict(await self._client.request("session.abort", params_dict, **_timeout_kwargs(timeout))) + @experimental async def shutdown(self, params: ShutdownRequest, *, timeout: float | None = None) -> None: "Shuts down the session and persists its final state. Awaits any deferred sessionEnd hooks before resolving so user-supplied hook scripts complete before the runtime tears down.\n\nArgs:\n params: Parameters for shutting down the session\n\n.. warning:: This API is experimental and may change or be removed in future versions." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id await self._client.request("session.shutdown", params_dict, **_timeout_kwargs(timeout)) + @experimental async def log(self, params: LogRequest, *, timeout: float | None = None) -> LogResult: "Emits a user-visible session log event.\n\nArgs:\n params: Message text, optional severity level, persistence flag, optional follow-up URL, and optional tip.\n\nReturns:\n Identifier of the session event that was emitted for the log message.\n\n.. warning:: This API is experimental and may change or be removed in future versions." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} diff --git a/python/test_experimental.py b/python/test_experimental.py new file mode 100644 index 000000000..5eecb87b6 --- /dev/null +++ b/python/test_experimental.py @@ -0,0 +1,127 @@ +"""Tests for the experimental-API runtime gating in :mod:`copilot.experimental`.""" + +from __future__ import annotations + +import inspect +import warnings + +import pytest + +import copilot +from copilot import ( + ExperimentalWarning, + allow_experimental, + experimental, + is_experimental, + set_experimental_policy, +) + + +def test_experimental_function_warns() -> None: + @experimental + def add_one(x: int) -> int: + return x + 1 + + with pytest.warns(ExperimentalWarning): + result = add_one(1) + assert result == 2 + assert is_experimental(add_one) + + +def test_experimental_with_since_includes_version_in_message() -> None: + @experimental(since="1.2") + def g() -> str: + return "ok" + + with pytest.warns(ExperimentalWarning, match="experimental since 1.2"): + assert g() == "ok" + + +async def test_experimental_async_function_warns() -> None: + @experimental + async def double(x: int) -> int: + return x * 2 + + with pytest.warns(ExperimentalWarning): + result = await double(3) + assert result == 6 + + +def test_experimental_class_warns_on_construction() -> None: + @experimental + class Widget: + def __init__(self, value: int) -> None: + self.value = value + + with pytest.warns(ExperimentalWarning): + instance = Widget(5) + assert instance.value == 5 + assert is_experimental(Widget) + assert is_experimental(instance) + + +def test_stable_callable_does_not_warn() -> None: + def stable() -> int: + return 1 + + assert not is_experimental(stable) + with warnings.catch_warnings(): + warnings.simplefilter("error") + assert stable() == 1 + + +def test_allow_experimental_silences_warning() -> None: + @experimental + def f() -> int: + return 7 + + with warnings.catch_warnings(): + warnings.simplefilter("error", ExperimentalWarning) + with allow_experimental(): + assert f() == 7 + + +def test_policy_error_raises() -> None: + @experimental + def f() -> int: + return 1 + + with warnings.catch_warnings(): + set_experimental_policy("error") + with pytest.raises(ExperimentalWarning): + f() + + +def test_policy_ignore_silences() -> None: + @experimental + def f() -> int: + return 1 + + with warnings.catch_warnings(): + warnings.simplefilter("error", ExperimentalWarning) + set_experimental_policy("ignore") + assert f() == 1 + + +def test_invalid_policy_raises_value_error() -> None: + with pytest.raises(ValueError): + set_experimental_policy("bogus") + + +def test_signature_is_preserved() -> None: + @experimental + def f(a: int, b: str = "x") -> str: + return f"{a}{b}" + + sig = inspect.signature(f) + assert list(sig.parameters) == ["a", "b"] + with allow_experimental(): + assert f(1) == "1x" + + +def test_public_exports() -> None: + assert copilot.experimental is experimental + assert copilot.ExperimentalWarning is ExperimentalWarning + assert copilot.is_experimental is is_experimental + assert copilot.allow_experimental is allow_experimental + assert copilot.set_experimental_policy is set_experimental_policy diff --git a/rust/Cargo.toml b/rust/Cargo.toml index d835ed276..7a0632195 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -30,6 +30,10 @@ default = ["bundled-cli"] bundled-cli = ["dep:tar", "dep:flate2", "dep:zip"] derive = ["dep:schemars"] test-support = [] +# Opt in to experimental, unstable APIs. Experimental methods are gated behind +# this feature so depending on them is a deliberate, explicit choice; they may +# change or be removed in any release without notice. +experimental = [] # Build docs.rs documentation with all features so feature-gated APIs # (e.g. `define_tool`, `schema_for`) appear and intra-doc links resolve. @@ -67,6 +71,13 @@ serial_test = "3" tempfile = "3" tokio = { version = "1", features = ["rt-multi-thread"] } +# Examples that demonstrate experimental APIs require the `experimental` +# feature so they compile against the gated, opt-in surface. Build them with: +# `cargo run --example manual_tool_resume --features experimental`. +[[example]] +name = "manual_tool_resume" +required-features = ["experimental"] + # Integration tests that call test-support-only Client methods (e.g. # `from_streams_with_connection_token`, `from_streams_with_trace_provider`) # require the `test-support` feature because `cfg(test)` is not set on the diff --git a/rust/README.md b/rust/README.md index 9bf1fddd9..c03e78eac 100644 --- a/rust/README.md +++ b/rust/README.md @@ -180,6 +180,29 @@ New RPCs land in the namespace immediately as the schema regenerates; helpers are added on top only when an ergonomic story is worth the maintenance. +#### Experimental APIs + +Some generated RPC methods are part of an **experimental**, unstable +wire-protocol surface that may change or be removed in any release. These +methods are gated behind the `experimental` Cargo feature, the Rust analog of +C# `[Experimental]` and Java `@CopilotExperimental`: depending on one is a +deliberate, explicit opt-in rather than something you can reach by accident. + +Without the feature the methods are not part of the public API, so calling one +is a hard compile error (`method ... is private` / `method not found`). Opt in +per-dependency: + +```toml +[dependencies] +github-copilot-sdk = { version = "...", features = ["experimental"] } +``` + +Several of the typed-RPC examples above (`agent().list()`, `tasks().list()`, +`sessions().fork()`, ...) are experimental and require this feature. Each +experimental method also carries an `**Experimental.**` admonition in its +rustdoc. Stable helpers (e.g. `Session`/`Client` convenience methods) are never +gated, even when they call an experimental RPC internally. + ### Handler Traits The SDK exposes five focused handler traits, one per CLI callback type. Implement only the traits you need and install each with the matching `SessionConfig` setter. Each trait has a single `async fn handle(...)` method: @@ -902,6 +925,7 @@ Supported: `darwin-arm64`, `darwin-x64`, `linux-x64`, `linux-arm64`, `win32-x64` | -------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | | `bundled-cli` | ✓ | Build-time CLI embedding. Pulls in `tar`+`flate2` (Linux/macOS) or `zip` (Windows). Disable via `default-features = false` to opt out (e.g. when shipping a smaller binary or when always supplying the CLI via `CliProgram::Path` / `COPILOT_CLI_PATH`). | | `derive` | — | `schema_for::()` for generating JSON Schema from Rust types (adds `schemars`). Enable when defining [tool parameters](#tool-registration). | +| `experimental` | — | Exposes [experimental, unstable RPC methods](#experimental-apis). Off by default so depending on an experimental API is a deliberate opt-in; without it, calling one is a compile error. APIs behind this feature may change or be removed in any release. | ```toml # These examples use registry syntax for illustration; until the crate is @@ -915,4 +939,7 @@ github-copilot-sdk = { version = "0.1", default-features = false } # Derive JSON Schema for tool parameters (adds to default bundled-cli). github-copilot-sdk = { version = "0.1", features = ["derive"] } + +# Opt in to experimental, unstable RPC APIs. +github-copilot-sdk = { version = "0.1", features = ["experimental"] } ``` diff --git a/rust/src/generated/rpc.rs b/rust/src/generated/rpc.rs index 82319dbbf..d3d98b24a 100644 --- a/rust/src/generated/rpc.rs +++ b/rust/src/generated/rpc.rs @@ -233,6 +233,7 @@ impl<'a> ClientRpcAgentRegistry<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn spawn( &self, params: AgentRegistrySpawnRequest, @@ -244,6 +245,20 @@ impl<'a> ClientRpcAgentRegistry<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn spawn( + &self, + params: AgentRegistrySpawnRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::AGENTREGISTRY_SPAWN, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `agents.*` RPCs. @@ -272,6 +287,7 @@ impl<'a> ClientRpcAgents<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn discover(&self, params: AgentsDiscoverRequest) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self @@ -281,6 +297,20 @@ impl<'a> ClientRpcAgents<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn discover( + &self, + params: AgentsDiscoverRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::AGENTS_DISCOVER, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Returns the canonical directories where a client may create custom agents that the runtime will recognize, including ones that do not exist yet. Project directories become active once created. /// /// Wire method: `agents.getDiscoveryPaths`. @@ -300,6 +330,7 @@ impl<'a> ClientRpcAgents<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn get_discovery_paths( &self, params: AgentsGetDiscoveryPathsRequest, @@ -311,6 +342,20 @@ impl<'a> ClientRpcAgents<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn get_discovery_paths( + &self, + params: AgentsGetDiscoveryPathsRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::AGENTS_GETDISCOVERYPATHS, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `instructions.*` RPCs. @@ -339,6 +384,7 @@ impl<'a> ClientRpcInstructions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn discover( &self, params: InstructionsDiscoverRequest, @@ -351,6 +397,20 @@ impl<'a> ClientRpcInstructions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn discover( + &self, + params: InstructionsDiscoverRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::INSTRUCTIONS_DISCOVER, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Returns the canonical files and directories where a client may create custom instructions that the runtime will recognize, including ones that do not exist yet. Repository targets become active once created. /// /// Wire method: `instructions.getDiscoveryPaths`. @@ -370,6 +430,7 @@ impl<'a> ClientRpcInstructions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn get_discovery_paths( &self, params: InstructionsGetDiscoveryPathsRequest, @@ -384,6 +445,23 @@ impl<'a> ClientRpcInstructions<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn get_discovery_paths( + &self, + params: InstructionsGetDiscoveryPathsRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call( + rpc_methods::INSTRUCTIONS_GETDISCOVERYPATHS, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `mcp.*` RPCs. @@ -610,6 +688,7 @@ impl<'a> ClientRpcPlugins<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn list(&self) -> Result { let wire_params = serde_json::json!({}); let _value = self @@ -619,6 +698,17 @@ impl<'a> ClientRpcPlugins<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn list(&self) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call(rpc_methods::PLUGINS_LIST, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Installs a plugin from a marketplace, GitHub repo, URL, or local path. /// /// Wire method: `plugins.install`. @@ -638,6 +728,7 @@ impl<'a> ClientRpcPlugins<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn install( &self, params: PluginsInstallRequest, @@ -650,6 +741,20 @@ impl<'a> ClientRpcPlugins<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn install( + &self, + params: PluginsInstallRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::PLUGINS_INSTALL, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Uninstalls an installed plugin. /// /// Wire method: `plugins.uninstall`. @@ -665,6 +770,7 @@ impl<'a> ClientRpcPlugins<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn uninstall(&self, params: PluginsUninstallRequest) -> Result<(), Error> { let wire_params = serde_json::to_value(params)?; let _value = self @@ -674,6 +780,17 @@ impl<'a> ClientRpcPlugins<'a> { Ok(()) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn uninstall(&self, params: PluginsUninstallRequest) -> Result<(), Error> { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::PLUGINS_UNINSTALL, Some(wire_params)) + .await?; + Ok(()) + } + /// Updates an installed plugin to its latest published version. /// /// Wire method: `plugins.update`. @@ -693,6 +810,7 @@ impl<'a> ClientRpcPlugins<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn update(&self, params: PluginsUpdateRequest) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self @@ -702,6 +820,20 @@ impl<'a> ClientRpcPlugins<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn update( + &self, + params: PluginsUpdateRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::PLUGINS_UPDATE, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Updates every installed plugin to its latest published version. /// /// Wire method: `plugins.updateAll`. @@ -717,6 +849,7 @@ impl<'a> ClientRpcPlugins<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn update_all(&self) -> Result { let wire_params = serde_json::json!({}); let _value = self @@ -726,6 +859,17 @@ impl<'a> ClientRpcPlugins<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn update_all(&self) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call(rpc_methods::PLUGINS_UPDATEALL, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Enables installed plugins for new sessions. /// /// Wire method: `plugins.enable`. @@ -741,6 +885,7 @@ impl<'a> ClientRpcPlugins<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn enable(&self, params: PluginsEnableRequest) -> Result<(), Error> { let wire_params = serde_json::to_value(params)?; let _value = self @@ -750,6 +895,17 @@ impl<'a> ClientRpcPlugins<'a> { Ok(()) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn enable(&self, params: PluginsEnableRequest) -> Result<(), Error> { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::PLUGINS_ENABLE, Some(wire_params)) + .await?; + Ok(()) + } + /// Disables installed plugins for new sessions. /// /// Wire method: `plugins.disable`. @@ -765,6 +921,7 @@ impl<'a> ClientRpcPlugins<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn disable(&self, params: PluginsDisableRequest) -> Result<(), Error> { let wire_params = serde_json::to_value(params)?; let _value = self @@ -773,6 +930,17 @@ impl<'a> ClientRpcPlugins<'a> { .await?; Ok(()) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn disable(&self, params: PluginsDisableRequest) -> Result<(), Error> { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::PLUGINS_DISABLE, Some(wire_params)) + .await?; + Ok(()) + } } /// `plugins.marketplaces.*` RPCs. @@ -797,6 +965,7 @@ impl<'a> ClientRpcPluginsMarketplaces<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn list(&self) -> Result { let wire_params = serde_json::json!({}); let _value = self @@ -806,6 +975,17 @@ impl<'a> ClientRpcPluginsMarketplaces<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn list(&self) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call(rpc_methods::PLUGINS_MARKETPLACES_LIST, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Registers a new marketplace from a source (owner/repo, URL, or local path). /// /// Wire method: `plugins.marketplaces.add`. @@ -825,6 +1005,7 @@ impl<'a> ClientRpcPluginsMarketplaces<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn add( &self, params: PluginsMarketplacesAddRequest, @@ -837,6 +1018,20 @@ impl<'a> ClientRpcPluginsMarketplaces<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn add( + &self, + params: PluginsMarketplacesAddRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::PLUGINS_MARKETPLACES_ADD, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Removes a previously-registered marketplace. When the marketplace has dependent plugins and `force` is not set, the marketplace is left intact and the result lists the dependents so the caller can decide whether to retry with `force=true`. /// /// Wire method: `plugins.marketplaces.remove`. @@ -856,6 +1051,7 @@ impl<'a> ClientRpcPluginsMarketplaces<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn remove( &self, params: PluginsMarketplacesRemoveRequest, @@ -868,6 +1064,20 @@ impl<'a> ClientRpcPluginsMarketplaces<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn remove( + &self, + params: PluginsMarketplacesRemoveRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::PLUGINS_MARKETPLACES_REMOVE, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Lists plugins advertised by a registered marketplace. /// /// Wire method: `plugins.marketplaces.browse`. @@ -887,6 +1097,7 @@ impl<'a> ClientRpcPluginsMarketplaces<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn browse( &self, params: PluginsMarketplacesBrowseRequest, @@ -899,6 +1110,20 @@ impl<'a> ClientRpcPluginsMarketplaces<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn browse( + &self, + params: PluginsMarketplacesBrowseRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::PLUGINS_MARKETPLACES_BROWSE, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Re-fetches one or all registered marketplace catalogs. /// /// Wire method: `plugins.marketplaces.refresh`. @@ -914,6 +1139,7 @@ impl<'a> ClientRpcPluginsMarketplaces<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn refresh(&self) -> Result { let wire_params = serde_json::json!({}); let _value = self @@ -923,6 +1149,17 @@ impl<'a> ClientRpcPluginsMarketplaces<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn refresh(&self) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call(rpc_methods::PLUGINS_MARKETPLACES_REFRESH, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Re-fetches one or all registered marketplace catalogs. /// /// Wire method: `plugins.marketplaces.refresh`. @@ -942,6 +1179,7 @@ impl<'a> ClientRpcPluginsMarketplaces<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn refresh_with_params( &self, params: PluginsMarketplacesRefreshRequest, @@ -953,6 +1191,20 @@ impl<'a> ClientRpcPluginsMarketplaces<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn refresh_with_params( + &self, + params: PluginsMarketplacesRefreshRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::PLUGINS_MARKETPLACES_REFRESH, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `runtime.*` RPCs. @@ -1059,6 +1311,7 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn open(&self) -> Result { let wire_params = serde_json::json!({}); let _value = self @@ -1068,6 +1321,17 @@ impl<'a> ClientRpcSessions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn open(&self) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call(rpc_methods::SESSIONS_OPEN, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Creates a new session by forking persisted history from an existing session. /// /// Wire method: `sessions.fork`. @@ -1087,6 +1351,7 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn fork(&self, params: SessionsForkRequest) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self @@ -1096,6 +1361,20 @@ impl<'a> ClientRpcSessions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn fork( + &self, + params: SessionsForkRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::SESSIONS_FORK, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Connects to an existing remote session and exposes it as an SDK session. /// /// Wire method: `sessions.connect`. @@ -1115,6 +1394,7 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn connect( &self, params: ConnectRemoteSessionParams, @@ -1127,6 +1407,20 @@ impl<'a> ClientRpcSessions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn connect( + &self, + params: ConnectRemoteSessionParams, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::SESSIONS_CONNECT, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Lists sessions, optionally filtered by source and working-directory context. Returned entries are discriminated by `isRemote`: local entries carry only the lightweight `LocalSessionMetadataValue` shape; remote entries carry the full `RemoteSessionMetadataValue` shape (repository, PR number, taskType, etc.). /// /// Wire method: `sessions.list`. @@ -1142,6 +1436,7 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn list(&self) -> Result { let wire_params = serde_json::json!({}); let _value = self @@ -1151,6 +1446,17 @@ impl<'a> ClientRpcSessions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn list(&self) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call(rpc_methods::SESSIONS_LIST, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Lists sessions, optionally filtered by source and working-directory context. Returned entries are discriminated by `isRemote`: local entries carry only the lightweight `LocalSessionMetadataValue` shape; remote entries carry the full `RemoteSessionMetadataValue` shape (repository, PR number, taskType, etc.). /// /// Wire method: `sessions.list`. @@ -1170,6 +1476,7 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn list_with_params( &self, params: SessionsListRequest, @@ -1182,6 +1489,20 @@ impl<'a> ClientRpcSessions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn list_with_params( + &self, + params: SessionsListRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::SESSIONS_LIST, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Finds the local session bound to a GitHub task ID, if any. /// /// Wire method: `sessions.findByTaskId`. @@ -1201,6 +1522,7 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn find_by_task_id( &self, params: SessionsFindByTaskIDRequest, @@ -1213,6 +1535,20 @@ impl<'a> ClientRpcSessions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn find_by_task_id( + &self, + params: SessionsFindByTaskIDRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::SESSIONS_FINDBYTASKID, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Resolves a UUID prefix to a unique session ID, if exactly one session matches. /// /// Wire method: `sessions.findByPrefix`. @@ -1232,6 +1568,7 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn find_by_prefix( &self, params: SessionsFindByPrefixRequest, @@ -1244,6 +1581,20 @@ impl<'a> ClientRpcSessions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn find_by_prefix( + &self, + params: SessionsFindByPrefixRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::SESSIONS_FINDBYPREFIX, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Returns the most-relevant prior session for a given working-directory context. /// /// Wire method: `sessions.getLastForContext`. @@ -1263,6 +1614,7 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn get_last_for_context( &self, params: SessionsGetLastForContextRequest, @@ -1275,6 +1627,20 @@ impl<'a> ClientRpcSessions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn get_last_for_context( + &self, + params: SessionsGetLastForContextRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::SESSIONS_GETLASTFORCONTEXT, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Computes the absolute path to a session's persisted events.jsonl file. Internal: filesystem paths are only meaningful in-process (CLI and runtime share a filesystem). Currently used by the CLI's contribution-graph feature to read historical events directly. Remote SDK consumers must not depend on this; a proper event-query API would replace it if the contribution graph ever needed to work over the wire. /// /// Wire method: `sessions.getEventFilePath`. @@ -1321,6 +1687,7 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn get_sizes(&self) -> Result { let wire_params = serde_json::json!({}); let _value = self @@ -1330,6 +1697,17 @@ impl<'a> ClientRpcSessions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn get_sizes(&self) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call(rpc_methods::SESSIONS_GETSIZES, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Returns the subset of the supplied session IDs that are currently held by another running process. /// /// Wire method: `sessions.checkInUse`. @@ -1349,6 +1727,7 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn check_in_use( &self, params: SessionsCheckInUseRequest, @@ -1361,6 +1740,20 @@ impl<'a> ClientRpcSessions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn check_in_use( + &self, + params: SessionsCheckInUseRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::SESSIONS_CHECKINUSE, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Returns a session's persisted remote-steerable flag, if any has been recorded. Internal: this is CLI-specific book-keeping used by `--continue` / `--resume` to inherit the prior session's remote-steerable preference. SDK consumers that want similar behavior should manage their own persistence around start/stop calls rather than relying on this runtime-side flag. /// /// Wire method: `sessions.getPersistedRemoteSteerable`. @@ -1414,6 +1807,7 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn close(&self, params: SessionsCloseRequest) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self @@ -1423,6 +1817,20 @@ impl<'a> ClientRpcSessions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn close( + &self, + params: SessionsCloseRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::SESSIONS_CLOSE, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Closes, deactivates, and deletes a set of sessions, returning the bytes freed per session. /// /// Wire method: `sessions.bulkDelete`. @@ -1442,6 +1850,7 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn bulk_delete( &self, params: SessionsBulkDeleteRequest, @@ -1454,6 +1863,20 @@ impl<'a> ClientRpcSessions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn bulk_delete( + &self, + params: SessionsBulkDeleteRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::SESSIONS_BULKDELETE, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Deletes sessions older than the given threshold, with optional dry-run and exclusion list. /// /// Wire method: `sessions.pruneOld`. @@ -1473,6 +1896,7 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn prune_old( &self, params: SessionsPruneOldRequest, @@ -1485,6 +1909,20 @@ impl<'a> ClientRpcSessions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn prune_old( + &self, + params: SessionsPruneOldRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::SESSIONS_PRUNEOLD, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Flushes a session's pending events to disk. /// /// Wire method: `sessions.save`. @@ -1504,6 +1942,7 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn save(&self, params: SessionsSaveRequest) -> Result { let wire_params = serde_json::to_value(params)?; let _value = self @@ -1513,6 +1952,20 @@ impl<'a> ClientRpcSessions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn save( + &self, + params: SessionsSaveRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::SESSIONS_SAVE, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Releases the in-use lock held by this process for a session. /// /// Wire method: `sessions.releaseLock`. @@ -1532,6 +1985,7 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn release_lock( &self, params: SessionsReleaseLockRequest, @@ -1544,6 +1998,20 @@ impl<'a> ClientRpcSessions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn release_lock( + &self, + params: SessionsReleaseLockRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::SESSIONS_RELEASELOCK, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Backfills missing summary and context fields on the supplied session metadata records. /// /// Wire method: `sessions.enrichMetadata`. @@ -1563,6 +2031,7 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn enrich_metadata( &self, params: SessionsEnrichMetadataRequest, @@ -1575,6 +2044,20 @@ impl<'a> ClientRpcSessions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn enrich_metadata( + &self, + params: SessionsEnrichMetadataRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::SESSIONS_ENRICHMETADATA, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Reloads user, plugin, and (optionally) repo hooks on the active session. /// /// Wire method: `sessions.reloadPluginHooks`. @@ -1594,6 +2077,7 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn reload_plugin_hooks( &self, params: SessionsReloadPluginHooksRequest, @@ -1606,6 +2090,20 @@ impl<'a> ClientRpcSessions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn reload_plugin_hooks( + &self, + params: SessionsReloadPluginHooksRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::SESSIONS_RELOADPLUGINHOOKS, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Loads previously-deferred repo-level hooks on the active session, returning queued startup prompts. /// /// Wire method: `sessions.loadDeferredRepoHooks`. @@ -1625,6 +2123,7 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn load_deferred_repo_hooks( &self, params: SessionsLoadDeferredRepoHooksRequest, @@ -1640,6 +2139,23 @@ impl<'a> ClientRpcSessions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn load_deferred_repo_hooks( + &self, + params: SessionsLoadDeferredRepoHooksRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call( + rpc_methods::SESSIONS_LOADDEFERREDREPOHOOKS, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Replaces the manager-wide additional plugins registered with the session manager. /// /// Wire method: `sessions.setAdditionalPlugins`. @@ -1659,6 +2175,7 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn set_additional_plugins( &self, params: SessionsSetAdditionalPluginsRequest, @@ -1674,6 +2191,23 @@ impl<'a> ClientRpcSessions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn set_additional_plugins( + &self, + params: SessionsSetAdditionalPluginsRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call( + rpc_methods::SESSIONS_SETADDITIONALPLUGINS, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Gets the dynamic-context board entry count associated with a session, when available. Internal: this exists solely so CLI telemetry events (`rem_spawn_gate`, `rem_consolidation_complete`) can pair START / END board counts around the detached rem-agent spawn. "Dynamic context board" is a runtime-internal concept that is not part of the public SDK contract; the long-term plan is to relocate the telemetry emission into the runtime so this method can be deleted entirely. /// /// Wire method: `sessions.getBoardEntryCount`. @@ -1724,6 +2258,7 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn start_remote_control( &self, params: SessionsStartRemoteControlRequest, @@ -1736,6 +2271,20 @@ impl<'a> ClientRpcSessions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn start_remote_control( + &self, + params: SessionsStartRemoteControlRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::SESSIONS_STARTREMOTECONTROL, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Atomically rebinds the remote-control singleton to a different session, preserving the underlying Mission Control connection. When `expectedFromSessionId` is provided and does not match the singleton's current `attachedSessionId`, the transfer is rejected with `transferred: false` and the current status is returned unchanged. /// /// Wire method: `sessions.transferRemoteControl`. @@ -1755,6 +2304,7 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn transfer_remote_control( &self, params: SessionsTransferRemoteControlRequest, @@ -1770,6 +2320,23 @@ impl<'a> ClientRpcSessions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn transfer_remote_control( + &self, + params: SessionsTransferRemoteControlRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call( + rpc_methods::SESSIONS_TRANSFERREMOTECONTROL, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Patches the steering state of the active remote-control singleton. When remote control is off, this is a no-op and the off status is returned. Today only `enabled: true` is actionable on the underlying exporter; passing `false` is reserved for future use. /// /// Wire method: `sessions.setRemoteControlSteering`. @@ -1789,6 +2356,7 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn set_remote_control_steering( &self, params: SessionsSetRemoteControlSteeringRequest, @@ -1804,6 +2372,23 @@ impl<'a> ClientRpcSessions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn set_remote_control_steering( + &self, + params: SessionsSetRemoteControlSteeringRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call( + rpc_methods::SESSIONS_SETREMOTECONTROLSTEERING, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Stops the remote-control singleton. When `expectedSessionId` is provided and does not match the singleton's current `attachedSessionId`, the stop is rejected with `stopped: false` and the current status is returned unchanged (unless `force` is set, in which case the singleton is unconditionally torn down). /// /// Wire method: `sessions.stopRemoteControl`. @@ -1819,6 +2404,7 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn stop_remote_control(&self) -> Result { let wire_params = serde_json::json!({}); let _value = self @@ -1828,6 +2414,17 @@ impl<'a> ClientRpcSessions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn stop_remote_control(&self) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call(rpc_methods::SESSIONS_STOPREMOTECONTROL, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Stops the remote-control singleton. When `expectedSessionId` is provided and does not match the singleton's current `attachedSessionId`, the stop is rejected with `stopped: false` and the current status is returned unchanged (unless `force` is set, in which case the singleton is unconditionally torn down). /// /// Wire method: `sessions.stopRemoteControl`. @@ -1847,6 +2444,7 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn stop_remote_control_with_params( &self, params: SessionsStopRemoteControlRequest, @@ -1859,6 +2457,20 @@ impl<'a> ClientRpcSessions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn stop_remote_control_with_params( + &self, + params: SessionsStopRemoteControlRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::SESSIONS_STOPREMOTECONTROL, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Returns the current state of the remote-control singleton, including the attached session id and frontend URL when active. /// /// Wire method: `sessions.getRemoteControlStatus`. @@ -1874,6 +2486,7 @@ impl<'a> ClientRpcSessions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn get_remote_control_status(&self) -> Result { let wire_params = serde_json::json!({}); let _value = self @@ -1886,6 +2499,22 @@ impl<'a> ClientRpcSessions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn get_remote_control_status( + &self, + ) -> Result { + let wire_params = serde_json::json!({}); + let _value = self + .client + .call( + rpc_methods::SESSIONS_GETREMOTECONTROLSTATUS, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Cursor-based long-poll for sessions spawned by the runtime (e.g. in response to a Mission Control `start_session` command). The cursor is an opaque token; pass it back to receive only spawn events that occurred AFTER the cursor was issued. Omit the cursor on the first call to receive any events buffered since the runtime started. Internal: this is a CLI background-daemon plumbing primitive. SDK consumers that need to react to runtime-spawned sessions should subscribe to a higher-level event stream rather than driving a long-poll loop. /// /// Wire method: `sessions.pollSpawnedSessions`. @@ -2059,6 +2688,7 @@ impl<'a> ClientRpcSkills<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn get_discovery_paths( &self, params: SkillsGetDiscoveryPathsRequest, @@ -2070,6 +2700,20 @@ impl<'a> ClientRpcSkills<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn get_discovery_paths( + &self, + params: SkillsGetDiscoveryPathsRequest, + ) -> Result { + let wire_params = serde_json::to_value(params)?; + let _value = self + .client + .call(rpc_methods::SKILLS_GETDISCOVERYPATHS, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `skills.config.*` RPCs. @@ -2400,6 +3044,7 @@ impl<'a> SessionRpc<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn suspend(&self) -> Result<(), Error> { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -2410,6 +3055,18 @@ impl<'a> SessionRpc<'a> { Ok(()) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn suspend(&self) -> Result<(), Error> { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_SUSPEND, Some(wire_params)) + .await?; + Ok(()) + } + /// Sends a user message to the session and returns its message ID. /// /// Wire method: `session.send`. @@ -2429,6 +3086,7 @@ impl<'a> SessionRpc<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn send(&self, params: SendRequest) -> Result { let mut wire_params = serde_json::to_value(params)?; wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); @@ -2440,6 +3098,19 @@ impl<'a> SessionRpc<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn send(&self, params: SendRequest) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_SEND, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Aborts the current agent turn. /// /// Wire method: `session.abort`. @@ -2459,6 +3130,7 @@ impl<'a> SessionRpc<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn abort(&self, params: AbortRequest) -> Result { let mut wire_params = serde_json::to_value(params)?; wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); @@ -2470,6 +3142,19 @@ impl<'a> SessionRpc<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn abort(&self, params: AbortRequest) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_ABORT, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Shuts down the session and persists its final state. Awaits any deferred sessionEnd hooks before resolving so user-supplied hook scripts complete before the runtime tears down. /// /// Wire method: `session.shutdown`. @@ -2485,6 +3170,7 @@ impl<'a> SessionRpc<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn shutdown(&self, params: ShutdownRequest) -> Result<(), Error> { let mut wire_params = serde_json::to_value(params)?; wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); @@ -2496,6 +3182,19 @@ impl<'a> SessionRpc<'a> { Ok(()) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn shutdown(&self, params: ShutdownRequest) -> Result<(), Error> { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_SHUTDOWN, Some(wire_params)) + .await?; + Ok(()) + } + /// Emits a user-visible session log event. /// /// Wire method: `session.log`. @@ -2515,6 +3214,7 @@ impl<'a> SessionRpc<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn log(&self, params: LogRequest) -> Result { let mut wire_params = serde_json::to_value(params)?; wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); @@ -2525,6 +3225,19 @@ impl<'a> SessionRpc<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn log(&self, params: LogRequest) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_LOG, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `session.agent.*` RPCs. @@ -2549,6 +3262,7 @@ impl<'a> SessionRpcAgent<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn list(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -2559,11 +3273,23 @@ impl<'a> SessionRpcAgent<'a> { Ok(serde_json::from_value(_value)?) } - /// Gets the currently selected custom agent for the session. - /// - /// Wire method: `session.agent.getCurrent`. - /// - /// # Returns + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn list(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_AGENT_LIST, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Gets the currently selected custom agent for the session. + /// + /// Wire method: `session.agent.getCurrent`. + /// + /// # Returns /// /// The currently selected custom agent, or null when using the default agent. /// @@ -2574,6 +3300,7 @@ impl<'a> SessionRpcAgent<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn get_current(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -2584,6 +3311,18 @@ impl<'a> SessionRpcAgent<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn get_current(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_AGENT_GETCURRENT, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Selects a custom agent for subsequent turns in the session. /// /// Wire method: `session.agent.select`. @@ -2603,6 +3342,7 @@ impl<'a> SessionRpcAgent<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn select(&self, params: AgentSelectRequest) -> Result { let mut wire_params = serde_json::to_value(params)?; wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); @@ -2614,6 +3354,22 @@ impl<'a> SessionRpcAgent<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn select( + &self, + params: AgentSelectRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_AGENT_SELECT, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Clears the selected custom agent and returns the session to the default agent. /// /// Wire method: `session.agent.deselect`. @@ -2625,6 +3381,7 @@ impl<'a> SessionRpcAgent<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn deselect(&self) -> Result<(), Error> { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -2635,6 +3392,18 @@ impl<'a> SessionRpcAgent<'a> { Ok(()) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn deselect(&self) -> Result<(), Error> { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_AGENT_DESELECT, Some(wire_params)) + .await?; + Ok(()) + } + /// Reloads custom agent definitions and returns the refreshed list. /// /// Wire method: `session.agent.reload`. @@ -2650,6 +3419,7 @@ impl<'a> SessionRpcAgent<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn reload(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -2659,6 +3429,18 @@ impl<'a> SessionRpcAgent<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn reload(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_AGENT_RELOAD, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `session.auth.*` RPCs. @@ -2683,6 +3465,7 @@ impl<'a> SessionRpcAuth<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn get_status(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -2693,6 +3476,18 @@ impl<'a> SessionRpcAuth<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn get_status(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_AUTH_GETSTATUS, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Updates the session's auth credentials used for outbound model and API requests. /// /// Wire method: `session.auth.setCredentials`. @@ -2712,6 +3507,7 @@ impl<'a> SessionRpcAuth<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn set_credentials( &self, params: SessionSetCredentialsParams, @@ -2725,6 +3521,22 @@ impl<'a> SessionRpcAuth<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn set_credentials( + &self, + params: SessionSetCredentialsParams, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_AUTH_SETCREDENTIALS, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `session.canvas.*` RPCs. @@ -2756,6 +3568,7 @@ impl<'a> SessionRpcCanvas<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn list(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -2766,6 +3579,18 @@ impl<'a> SessionRpcCanvas<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn list(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_CANVAS_LIST, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Lists currently open canvas instances for the live session. /// /// Wire method: `session.canvas.listOpen`. @@ -2781,6 +3606,7 @@ impl<'a> SessionRpcCanvas<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn list_open(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -2791,6 +3617,18 @@ impl<'a> SessionRpcCanvas<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn list_open(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_CANVAS_LISTOPEN, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Opens or focuses a canvas instance. /// /// Wire method: `session.canvas.open`. @@ -2810,6 +3648,7 @@ impl<'a> SessionRpcCanvas<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn open(&self, params: CanvasOpenRequest) -> Result { let mut wire_params = serde_json::to_value(params)?; wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); @@ -2821,6 +3660,22 @@ impl<'a> SessionRpcCanvas<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn open( + &self, + params: CanvasOpenRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_CANVAS_OPEN, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Closes an open canvas instance. /// /// Wire method: `session.canvas.close`. @@ -2836,6 +3691,7 @@ impl<'a> SessionRpcCanvas<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn close(&self, params: CanvasCloseRequest) -> Result<(), Error> { let mut wire_params = serde_json::to_value(params)?; wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); @@ -2846,6 +3702,19 @@ impl<'a> SessionRpcCanvas<'a> { .await?; Ok(()) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn close(&self, params: CanvasCloseRequest) -> Result<(), Error> { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_CANVAS_CLOSE, Some(wire_params)) + .await?; + Ok(()) + } } /// `session.canvas.action.*` RPCs. @@ -2874,6 +3743,7 @@ impl<'a> SessionRpcCanvasAction<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn invoke( &self, params: CanvasActionInvokeRequest, @@ -2887,6 +3757,22 @@ impl<'a> SessionRpcCanvasAction<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn invoke( + &self, + params: CanvasActionInvokeRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_CANVAS_ACTION_INVOKE, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `session.commands.*` RPCs. @@ -2911,6 +3797,7 @@ impl<'a> SessionRpcCommands<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn list(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -2921,6 +3808,18 @@ impl<'a> SessionRpcCommands<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn list(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_COMMANDS_LIST, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Lists slash commands available in the session. /// /// Wire method: `session.commands.list`. @@ -2940,6 +3839,7 @@ impl<'a> SessionRpcCommands<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn list_with_params( &self, params: CommandsListRequest, @@ -2954,6 +3854,22 @@ impl<'a> SessionRpcCommands<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn list_with_params( + &self, + params: CommandsListRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_COMMANDS_LIST, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Invokes a slash command in the session. /// /// Wire method: `session.commands.invoke`. @@ -2973,6 +3889,7 @@ impl<'a> SessionRpcCommands<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn invoke( &self, params: CommandsInvokeRequest, @@ -2987,6 +3904,22 @@ impl<'a> SessionRpcCommands<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn invoke( + &self, + params: CommandsInvokeRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_COMMANDS_INVOKE, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Reports completion of a pending client-handled slash command. /// /// Wire method: `session.commands.handlePendingCommand`. @@ -3006,6 +3939,7 @@ impl<'a> SessionRpcCommands<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn handle_pending_command( &self, params: CommandsHandlePendingCommandRequest, @@ -3023,6 +3957,25 @@ impl<'a> SessionRpcCommands<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn handle_pending_command( + &self, + params: CommandsHandlePendingCommandRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_COMMANDS_HANDLEPENDINGCOMMAND, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Executes a slash command synchronously and returns any error. /// /// Wire method: `session.commands.execute`. @@ -3042,6 +3995,7 @@ impl<'a> SessionRpcCommands<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn execute( &self, params: ExecuteCommandParams, @@ -3056,6 +4010,22 @@ impl<'a> SessionRpcCommands<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn execute( + &self, + params: ExecuteCommandParams, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_COMMANDS_EXECUTE, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Enqueues a slash command for FIFO processing on the local session. /// /// Wire method: `session.commands.enqueue`. @@ -3075,6 +4045,7 @@ impl<'a> SessionRpcCommands<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn enqueue( &self, params: EnqueueCommandParams, @@ -3089,6 +4060,22 @@ impl<'a> SessionRpcCommands<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn enqueue( + &self, + params: EnqueueCommandParams, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_COMMANDS_ENQUEUE, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Reports whether the host actually executed a queued command and whether to continue processing. /// /// Wire method: `session.commands.respondToQueuedCommand`. @@ -3108,6 +4095,7 @@ impl<'a> SessionRpcCommands<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn respond_to_queued_command( &self, params: CommandsRespondToQueuedCommandRequest, @@ -3124,6 +4112,25 @@ impl<'a> SessionRpcCommands<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn respond_to_queued_command( + &self, + params: CommandsRespondToQueuedCommandRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_COMMANDS_RESPONDTOQUEUEDCOMMAND, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `session.eventLog.*` RPCs. @@ -3152,6 +4159,7 @@ impl<'a> SessionRpcEventLog<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn read(&self, params: EventLogReadRequest) -> Result { let mut wire_params = serde_json::to_value(params)?; wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); @@ -3163,6 +4171,22 @@ impl<'a> SessionRpcEventLog<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn read( + &self, + params: EventLogReadRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_EVENTLOG_READ, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Returns a snapshot of the current tail cursor without consuming events. /// /// Wire method: `session.eventLog.tail`. @@ -3178,6 +4202,7 @@ impl<'a> SessionRpcEventLog<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn tail(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -3188,6 +4213,18 @@ impl<'a> SessionRpcEventLog<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn tail(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_EVENTLOG_TAIL, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Registers consumer interest in an event type for runtime gating purposes. /// /// Wire method: `session.eventLog.registerInterest`. @@ -3207,6 +4244,7 @@ impl<'a> SessionRpcEventLog<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn register_interest( &self, params: RegisterEventInterestParams, @@ -3224,6 +4262,25 @@ impl<'a> SessionRpcEventLog<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn register_interest( + &self, + params: RegisterEventInterestParams, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_EVENTLOG_REGISTERINTEREST, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Releases a consumer's previously-registered interest in an event type. /// /// Wire method: `session.eventLog.releaseInterest`. @@ -3243,6 +4300,7 @@ impl<'a> SessionRpcEventLog<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn release_interest( &self, params: ReleaseEventInterestParams, @@ -3259,7 +4317,26 @@ impl<'a> SessionRpcEventLog<'a> { .await?; Ok(serde_json::from_value(_value)?) } -} + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn release_interest( + &self, + params: ReleaseEventInterestParams, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_EVENTLOG_RELEASEINTEREST, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } +} /// `session.extensions.*` RPCs. #[derive(Clone, Copy)] @@ -3283,6 +4360,7 @@ impl<'a> SessionRpcExtensions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn list(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -3293,6 +4371,18 @@ impl<'a> SessionRpcExtensions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn list(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_EXTENSIONS_LIST, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Enables an extension for the session. /// /// Wire method: `session.extensions.enable`. @@ -3308,6 +4398,7 @@ impl<'a> SessionRpcExtensions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn enable(&self, params: ExtensionsEnableRequest) -> Result<(), Error> { let mut wire_params = serde_json::to_value(params)?; wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); @@ -3319,6 +4410,19 @@ impl<'a> SessionRpcExtensions<'a> { Ok(()) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn enable(&self, params: ExtensionsEnableRequest) -> Result<(), Error> { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_EXTENSIONS_ENABLE, Some(wire_params)) + .await?; + Ok(()) + } + /// Disables an extension for the session. /// /// Wire method: `session.extensions.disable`. @@ -3334,6 +4438,7 @@ impl<'a> SessionRpcExtensions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn disable(&self, params: ExtensionsDisableRequest) -> Result<(), Error> { let mut wire_params = serde_json::to_value(params)?; wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); @@ -3345,6 +4450,19 @@ impl<'a> SessionRpcExtensions<'a> { Ok(()) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn disable(&self, params: ExtensionsDisableRequest) -> Result<(), Error> { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_EXTENSIONS_DISABLE, Some(wire_params)) + .await?; + Ok(()) + } + /// Reloads extension definitions and processes for the session. /// /// Wire method: `session.extensions.reload`. @@ -3356,6 +4474,7 @@ impl<'a> SessionRpcExtensions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn reload(&self) -> Result<(), Error> { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -3366,6 +4485,18 @@ impl<'a> SessionRpcExtensions<'a> { Ok(()) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn reload(&self) -> Result<(), Error> { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_EXTENSIONS_RELOAD, Some(wire_params)) + .await?; + Ok(()) + } + /// Push attachments into the next user-message turn from an extension. The host should surface them as composer pills and forward them via the next session.send call. Callable only by extension-owned connections. /// /// Wire method: `session.extensions.sendAttachmentsToMessage`. @@ -3381,6 +4512,7 @@ impl<'a> SessionRpcExtensions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn send_attachments_to_message( &self, params: SendAttachmentsToMessageParams, @@ -3397,6 +4529,25 @@ impl<'a> SessionRpcExtensions<'a> { .await?; Ok(()) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn send_attachments_to_message( + &self, + params: SendAttachmentsToMessageParams, + ) -> Result<(), Error> { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_EXTENSIONS_SENDATTACHMENTSTOMESSAGE, + Some(wire_params), + ) + .await?; + Ok(()) + } } /// `session.fleet.*` RPCs. @@ -3425,6 +4576,7 @@ impl<'a> SessionRpcFleet<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn start(&self, params: FleetStartRequest) -> Result { let mut wire_params = serde_json::to_value(params)?; wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); @@ -3435,6 +4587,19 @@ impl<'a> SessionRpcFleet<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn start(&self, params: FleetStartRequest) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_FLEET_START, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `session.history.*` RPCs. @@ -3459,6 +4624,7 @@ impl<'a> SessionRpcHistory<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn compact(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -3469,6 +4635,18 @@ impl<'a> SessionRpcHistory<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn compact(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_HISTORY_COMPACT, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Compacts the session history to reduce context usage. /// /// Wire method: `session.history.compact`. @@ -3488,6 +4666,7 @@ impl<'a> SessionRpcHistory<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn compact_with_params( &self, params: HistoryCompactRequest, @@ -3502,6 +4681,22 @@ impl<'a> SessionRpcHistory<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn compact_with_params( + &self, + params: HistoryCompactRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_HISTORY_COMPACT, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Truncates persisted session history to a specific event. /// /// Wire method: `session.history.truncate`. @@ -3521,6 +4716,7 @@ impl<'a> SessionRpcHistory<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn truncate( &self, params: HistoryTruncateRequest, @@ -3535,6 +4731,22 @@ impl<'a> SessionRpcHistory<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn truncate( + &self, + params: HistoryTruncateRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_HISTORY_TRUNCATE, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Cancels any in-progress background compaction on a local session. /// /// Wire method: `session.history.cancelBackgroundCompaction`. @@ -3550,6 +4762,7 @@ impl<'a> SessionRpcHistory<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn cancel_background_compaction( &self, ) -> Result { @@ -3565,6 +4778,23 @@ impl<'a> SessionRpcHistory<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn cancel_background_compaction( + &self, + ) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_HISTORY_CANCELBACKGROUNDCOMPACTION, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Aborts any in-progress manual compaction on a local session. /// /// Wire method: `session.history.abortManualCompaction`. @@ -3580,6 +4810,7 @@ impl<'a> SessionRpcHistory<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn abort_manual_compaction( &self, ) -> Result { @@ -3595,6 +4826,23 @@ impl<'a> SessionRpcHistory<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn abort_manual_compaction( + &self, + ) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_HISTORY_ABORTMANUALCOMPACTION, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Produces a markdown summary of the session's conversation context for hand-off scenarios. /// /// Wire method: `session.history.summarizeForHandoff`. @@ -3610,6 +4858,7 @@ impl<'a> SessionRpcHistory<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn summarize_for_handoff(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -3622,6 +4871,23 @@ impl<'a> SessionRpcHistory<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn summarize_for_handoff( + &self, + ) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_HISTORY_SUMMARIZEFORHANDOFF, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `session.instructions.*` RPCs. @@ -3646,6 +4912,7 @@ impl<'a> SessionRpcInstructions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn get_sources(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -3658,6 +4925,21 @@ impl<'a> SessionRpcInstructions<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn get_sources(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_INSTRUCTIONS_GETSOURCES, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `session.lsp.*` RPCs. @@ -3682,6 +4964,7 @@ impl<'a> SessionRpcLsp<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn initialize(&self, params: LspInitializeRequest) -> Result<(), Error> { let mut wire_params = serde_json::to_value(params)?; wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); @@ -3692,6 +4975,19 @@ impl<'a> SessionRpcLsp<'a> { .await?; Ok(()) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn initialize(&self, params: LspInitializeRequest) -> Result<(), Error> { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_LSP_INITIALIZE, Some(wire_params)) + .await?; + Ok(()) + } } /// `session.mcp.*` RPCs. @@ -3730,6 +5026,7 @@ impl<'a> SessionRpcMcp<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn list(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -3740,6 +5037,18 @@ impl<'a> SessionRpcMcp<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn list(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_MCP_LIST, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Lists the tools exposed by a connected MCP server on this session's host. /// /// Wire method: `session.mcp.listTools`. @@ -3759,6 +5068,7 @@ impl<'a> SessionRpcMcp<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn list_tools( &self, params: McpListToolsRequest, @@ -3773,6 +5083,22 @@ impl<'a> SessionRpcMcp<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn list_tools( + &self, + params: McpListToolsRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_MCP_LISTTOOLS, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Enables an MCP server for the session. /// /// Wire method: `session.mcp.enable`. @@ -3788,6 +5114,7 @@ impl<'a> SessionRpcMcp<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn enable(&self, params: McpEnableRequest) -> Result<(), Error> { let mut wire_params = serde_json::to_value(params)?; wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); @@ -3799,6 +5126,19 @@ impl<'a> SessionRpcMcp<'a> { Ok(()) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn enable(&self, params: McpEnableRequest) -> Result<(), Error> { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_MCP_ENABLE, Some(wire_params)) + .await?; + Ok(()) + } + /// Disables an MCP server for the session. /// /// Wire method: `session.mcp.disable`. @@ -3814,6 +5154,7 @@ impl<'a> SessionRpcMcp<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn disable(&self, params: McpDisableRequest) -> Result<(), Error> { let mut wire_params = serde_json::to_value(params)?; wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); @@ -3825,6 +5166,19 @@ impl<'a> SessionRpcMcp<'a> { Ok(()) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn disable(&self, params: McpDisableRequest) -> Result<(), Error> { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_MCP_DISABLE, Some(wire_params)) + .await?; + Ok(()) + } + /// Reloads MCP server connections for the session. /// /// Wire method: `session.mcp.reload`. @@ -3836,6 +5190,7 @@ impl<'a> SessionRpcMcp<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn reload(&self) -> Result<(), Error> { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -3846,6 +5201,18 @@ impl<'a> SessionRpcMcp<'a> { Ok(()) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn reload(&self) -> Result<(), Error> { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_MCP_RELOAD, Some(wire_params)) + .await?; + Ok(()) + } + /// Reloads MCP server connections for the session with an explicit host-provided configuration. /// /// Wire method: `session.mcp.reloadWithConfig`. @@ -3898,6 +5265,7 @@ impl<'a> SessionRpcMcp<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn execute_sampling( &self, params: McpExecuteSamplingParams, @@ -3912,6 +5280,22 @@ impl<'a> SessionRpcMcp<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn execute_sampling( + &self, + params: McpExecuteSamplingParams, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_MCP_EXECUTESAMPLING, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Cancels an in-flight MCP sampling execution by request ID. /// /// Wire method: `session.mcp.cancelSamplingExecution`. @@ -3931,6 +5315,7 @@ impl<'a> SessionRpcMcp<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn cancel_sampling_execution( &self, params: McpCancelSamplingExecutionParams, @@ -3948,25 +5333,45 @@ impl<'a> SessionRpcMcp<'a> { Ok(serde_json::from_value(_value)?) } - /// Sets how environment-variable values supplied to MCP servers are resolved (direct or indirect). - /// - /// Wire method: `session.mcp.setEnvValueMode`. - /// - /// # Parameters - /// - /// * `params` - Mode controlling how MCP server env values are resolved (`direct` or `indirect`). - /// - /// # Returns - /// - /// Env-value mode recorded on the session after the update. - /// - ///
+ #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn cancel_sampling_execution( + &self, + params: McpCancelSamplingExecutionParams, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_MCP_CANCELSAMPLINGEXECUTION, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Sets how environment-variable values supplied to MCP servers are resolved (direct or indirect). + /// + /// Wire method: `session.mcp.setEnvValueMode`. + /// + /// # Parameters + /// + /// * `params` - Mode controlling how MCP server env values are resolved (`direct` or `indirect`). + /// + /// # Returns + /// + /// Env-value mode recorded on the session after the update. + /// + ///
/// /// **Experimental.** This API is part of an experimental wire-protocol surface /// and may change or be removed in future SDK or CLI releases. Pin both the /// SDK and CLI versions if your code depends on it. /// ///
+ #[cfg(feature = "experimental")] pub async fn set_env_value_mode( &self, params: McpSetEnvValueModeParams, @@ -3981,6 +5386,22 @@ impl<'a> SessionRpcMcp<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn set_env_value_mode( + &self, + params: McpSetEnvValueModeParams, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_MCP_SETENVVALUEMODE, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Removes the auto-managed `github` MCP server when present. /// /// Wire method: `session.mcp.removeGitHub`. @@ -3996,6 +5417,7 @@ impl<'a> SessionRpcMcp<'a> { /// SDK and CLI versions if your code depends on it. /// ///
+ #[cfg(feature = "experimental")] pub async fn remove_git_hub(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -4006,6 +5428,18 @@ impl<'a> SessionRpcMcp<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn remove_git_hub(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_MCP_REMOVEGITHUB, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Configures the built-in GitHub MCP server for the session's current auth context. /// /// Wire method: `session.mcp.configureGitHub`. @@ -4109,6 +5543,7 @@ impl<'a> SessionRpcMcp<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn stop_server(&self, params: McpStopServerRequest) -> Result<(), Error> { let mut wire_params = serde_json::to_value(params)?; wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); @@ -4120,6 +5555,19 @@ impl<'a> SessionRpcMcp<'a> { Ok(()) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn stop_server(&self, params: McpStopServerRequest) -> Result<(), Error> { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_MCP_STOPSERVER, Some(wire_params)) + .await?; + Ok(()) + } + /// Registers a pre-connected external MCP client (e.g. IDE) on the session's host. The caller retains lifecycle ownership of the client and transport. Marked internal because the `client` and `transport` arguments are in-process MCP SDK instances that cannot be serialized across the JSON-RPC boundary; once the CLI moves on top of the SDK, external clients will be expressed as transport configs the runtime can construct itself. /// /// Wire method: `session.mcp.registerExternalClient`. @@ -4203,6 +5651,7 @@ impl<'a> SessionRpcMcp<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn is_server_running( &self, params: McpIsServerRunningRequest, @@ -4216,6 +5665,22 @@ impl<'a> SessionRpcMcp<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn is_server_running( + &self, + params: McpIsServerRunningRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_MCP_ISSERVERRUNNING, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `session.mcp.apps.*` RPCs. @@ -4244,6 +5709,7 @@ impl<'a> SessionRpcMcpApps<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn read_resource( &self, params: McpAppsReadResourceRequest, @@ -4261,6 +5727,25 @@ impl<'a> SessionRpcMcpApps<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn read_resource( + &self, + params: McpAppsReadResourceRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_MCP_APPS_READRESOURCE, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// List tools that an MCP App view is allowed to call (SEP-1865 visibility filter). Returns tools whose `_meta.ui.visibility` is unset (default `["model","app"]`) or includes `"app"`. /// /// Wire method: `session.mcp.apps.listTools`. @@ -4280,6 +5765,7 @@ impl<'a> SessionRpcMcpApps<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn list_tools( &self, params: McpAppsListToolsRequest, @@ -4294,6 +5780,22 @@ impl<'a> SessionRpcMcpApps<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn list_tools( + &self, + params: McpAppsListToolsRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_MCP_APPS_LISTTOOLS, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Call an MCP tool from an MCP App view (SEP-1865). Enforces the visibility check that prevents an app iframe from invoking model-only tools. Returns the standard MCP `CallToolResult`. /// /// Wire method: `session.mcp.apps.callTool`. @@ -4313,6 +5815,7 @@ impl<'a> SessionRpcMcpApps<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn call_tool( &self, params: McpAppsCallToolRequest, @@ -4327,6 +5830,22 @@ impl<'a> SessionRpcMcpApps<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn call_tool( + &self, + params: McpAppsCallToolRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_MCP_APPS_CALLTOOL, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Replace the host context returned to MCP App guests on `ui/initialize`. Hosts use this to advertise theme, locale, or other metadata to the guest UI. /// /// Wire method: `session.mcp.apps.setHostContext`. @@ -4342,6 +5861,7 @@ impl<'a> SessionRpcMcpApps<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn set_host_context( &self, params: McpAppsSetHostContextRequest, @@ -4359,6 +5879,25 @@ impl<'a> SessionRpcMcpApps<'a> { Ok(()) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn set_host_context( + &self, + params: McpAppsSetHostContextRequest, + ) -> Result<(), Error> { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_MCP_APPS_SETHOSTCONTEXT, + Some(wire_params), + ) + .await?; + Ok(()) + } + /// Read the current host context advertised to MCP App guests. /// /// Wire method: `session.mcp.apps.getHostContext`. @@ -4374,6 +5913,7 @@ impl<'a> SessionRpcMcpApps<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn get_host_context(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -4387,6 +5927,21 @@ impl<'a> SessionRpcMcpApps<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn get_host_context(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_MCP_APPS_GETHOSTCONTEXT, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Diagnose MCP Apps wiring for a specific MCP server. Reports the session capability, feature-flag state, advertised extension, and how many tools have `_meta.ui` populated. /// /// Wire method: `session.mcp.apps.diagnose`. @@ -4406,6 +5961,7 @@ impl<'a> SessionRpcMcpApps<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn diagnose( &self, params: McpAppsDiagnoseRequest, @@ -4419,6 +5975,22 @@ impl<'a> SessionRpcMcpApps<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn diagnose( + &self, + params: McpAppsDiagnoseRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_MCP_APPS_DIAGNOSE, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `session.mcp.oauth.*` RPCs. @@ -4480,6 +6052,7 @@ impl<'a> SessionRpcMcpOauth<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn login(&self, params: McpOauthLoginRequest) -> Result { let mut wire_params = serde_json::to_value(params)?; wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); @@ -4490,6 +6063,22 @@ impl<'a> SessionRpcMcpOauth<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn login( + &self, + params: McpOauthLoginRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_MCP_OAUTH_LOGIN, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `session.metadata.*` RPCs. @@ -4514,6 +6103,7 @@ impl<'a> SessionRpcMetadata<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn snapshot(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -4524,6 +6114,18 @@ impl<'a> SessionRpcMetadata<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn snapshot(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_METADATA_SNAPSHOT, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Reports whether the local session is currently processing user/agent messages. /// /// Wire method: `session.metadata.isProcessing`. @@ -4539,6 +6141,7 @@ impl<'a> SessionRpcMetadata<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn is_processing(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -4552,6 +6155,21 @@ impl<'a> SessionRpcMetadata<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn is_processing(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_METADATA_ISPROCESSING, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Returns a snapshot of activity flags for the session. /// /// Wire method: `session.metadata.activity`. @@ -4567,6 +6185,7 @@ impl<'a> SessionRpcMetadata<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn activity(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -4577,6 +6196,18 @@ impl<'a> SessionRpcMetadata<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn activity(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_METADATA_ACTIVITY, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Returns the token breakdown for the session's current context window for a given model. /// /// Wire method: `session.metadata.contextInfo`. @@ -4596,6 +6227,7 @@ impl<'a> SessionRpcMetadata<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn context_info( &self, params: MetadataContextInfoRequest, @@ -4610,6 +6242,22 @@ impl<'a> SessionRpcMetadata<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn context_info( + &self, + params: MetadataContextInfoRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_METADATA_CONTEXTINFO, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Records a working-directory/git context change and emits a `session.context_changed` event. /// /// Wire method: `session.metadata.recordContextChange`. @@ -4629,6 +6277,7 @@ impl<'a> SessionRpcMetadata<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn record_context_change( &self, params: MetadataRecordContextChangeRequest, @@ -4646,6 +6295,25 @@ impl<'a> SessionRpcMetadata<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn record_context_change( + &self, + params: MetadataRecordContextChangeRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_METADATA_RECORDCONTEXTCHANGE, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Updates the session's recorded working directory. /// /// Wire method: `session.metadata.setWorkingDirectory`. @@ -4665,6 +6333,7 @@ impl<'a> SessionRpcMetadata<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn set_working_directory( &self, params: MetadataSetWorkingDirectoryRequest, @@ -4682,6 +6351,25 @@ impl<'a> SessionRpcMetadata<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn set_working_directory( + &self, + params: MetadataSetWorkingDirectoryRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_METADATA_SETWORKINGDIRECTORY, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Re-tokenizes the session's existing messages against a model and returns aggregate token totals. /// /// Wire method: `session.metadata.recomputeContextTokens`. @@ -4701,6 +6389,7 @@ impl<'a> SessionRpcMetadata<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn recompute_context_tokens( &self, params: MetadataRecomputeContextTokensRequest, @@ -4717,20 +6406,39 @@ impl<'a> SessionRpcMetadata<'a> { .await?; Ok(serde_json::from_value(_value)?) } -} - -/// `session.mode.*` RPCs. -#[derive(Clone, Copy)] -pub struct SessionRpcMode<'a> { - pub(crate) session: &'a Session, -} -impl<'a> SessionRpcMode<'a> { - /// Gets the current agent interaction mode. - /// - /// Wire method: `session.mode.get`. - /// - /// # Returns + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn recompute_context_tokens( + &self, + params: MetadataRecomputeContextTokensRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_METADATA_RECOMPUTECONTEXTTOKENS, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } +} + +/// `session.mode.*` RPCs. +#[derive(Clone, Copy)] +pub struct SessionRpcMode<'a> { + pub(crate) session: &'a Session, +} + +impl<'a> SessionRpcMode<'a> { + /// Gets the current agent interaction mode. + /// + /// Wire method: `session.mode.get`. + /// + /// # Returns /// /// The session mode the agent is operating in /// @@ -4741,6 +6449,7 @@ impl<'a> SessionRpcMode<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn get(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -4751,6 +6460,18 @@ impl<'a> SessionRpcMode<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn get(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_MODE_GET, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Sets the current agent interaction mode. /// /// Wire method: `session.mode.set`. @@ -4766,6 +6487,7 @@ impl<'a> SessionRpcMode<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn set(&self, params: ModeSetRequest) -> Result<(), Error> { let mut wire_params = serde_json::to_value(params)?; wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); @@ -4776,6 +6498,19 @@ impl<'a> SessionRpcMode<'a> { .await?; Ok(()) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn set(&self, params: ModeSetRequest) -> Result<(), Error> { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_MODE_SET, Some(wire_params)) + .await?; + Ok(()) + } } /// `session.model.*` RPCs. @@ -4800,6 +6535,7 @@ impl<'a> SessionRpcModel<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn get_current(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -4810,6 +6546,18 @@ impl<'a> SessionRpcModel<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn get_current(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_MODEL_GETCURRENT, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Switches the session to a model and optional reasoning configuration. /// /// Wire method: `session.model.switchTo`. @@ -4829,6 +6577,7 @@ impl<'a> SessionRpcModel<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn switch_to( &self, params: ModelSwitchToRequest, @@ -4843,6 +6592,22 @@ impl<'a> SessionRpcModel<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn switch_to( + &self, + params: ModelSwitchToRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_MODEL_SWITCHTO, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Updates the session's reasoning effort without changing the selected model. /// /// Wire method: `session.model.setReasoningEffort`. @@ -4862,6 +6627,7 @@ impl<'a> SessionRpcModel<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn set_reasoning_effort( &self, params: ModelSetReasoningEffortRequest, @@ -4879,6 +6645,25 @@ impl<'a> SessionRpcModel<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn set_reasoning_effort( + &self, + params: ModelSetReasoningEffortRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_MODEL_SETREASONINGEFFORT, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Lists models available to this session using its own auth and integration context. Connected hosts (CLI TUI, GitHub App) should call this through the session client so remote sessions return the remote CLI's available models rather than the caller's. /// /// Wire method: `session.model.list`. @@ -4894,6 +6679,7 @@ impl<'a> SessionRpcModel<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn list(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -4904,6 +6690,18 @@ impl<'a> SessionRpcModel<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn list(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_MODEL_LIST, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Lists models available to this session using its own auth and integration context. Connected hosts (CLI TUI, GitHub App) should call this through the session client so remote sessions return the remote CLI's available models rather than the caller's. /// /// Wire method: `session.model.list`. @@ -4923,6 +6721,7 @@ impl<'a> SessionRpcModel<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn list_with_params( &self, params: ModelListRequest, @@ -4936,6 +6735,22 @@ impl<'a> SessionRpcModel<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn list_with_params( + &self, + params: ModelListRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_MODEL_LIST, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `session.name.*` RPCs. @@ -4960,6 +6775,7 @@ impl<'a> SessionRpcName<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn get(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -4970,6 +6786,18 @@ impl<'a> SessionRpcName<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn get(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_NAME_GET, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Sets the session's friendly name. /// /// Wire method: `session.name.set`. @@ -4985,6 +6813,7 @@ impl<'a> SessionRpcName<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn set(&self, params: NameSetRequest) -> Result<(), Error> { let mut wire_params = serde_json::to_value(params)?; wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); @@ -4996,6 +6825,19 @@ impl<'a> SessionRpcName<'a> { Ok(()) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn set(&self, params: NameSetRequest) -> Result<(), Error> { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_NAME_SET, Some(wire_params)) + .await?; + Ok(()) + } + /// Persists an auto-generated session summary as the session's name when no user-set name exists. /// /// Wire method: `session.name.setAuto`. @@ -5015,6 +6857,7 @@ impl<'a> SessionRpcName<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn set_auto(&self, params: NameSetAutoRequest) -> Result { let mut wire_params = serde_json::to_value(params)?; wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); @@ -5025,6 +6868,22 @@ impl<'a> SessionRpcName<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn set_auto( + &self, + params: NameSetAutoRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_NAME_SETAUTO, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `session.options.*` RPCs. @@ -5053,6 +6912,7 @@ impl<'a> SessionRpcOptions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn update( &self, params: SessionUpdateOptionsParams, @@ -5066,6 +6926,22 @@ impl<'a> SessionRpcOptions<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn update( + &self, + params: SessionUpdateOptionsParams, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_OPTIONS_UPDATE, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `session.permissions.*` RPCs. @@ -5122,6 +6998,7 @@ impl<'a> SessionRpcPermissions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn configure( &self, params: PermissionsConfigureParams, @@ -5139,6 +7016,25 @@ impl<'a> SessionRpcPermissions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn configure( + &self, + params: PermissionsConfigureParams, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_PERMISSIONS_CONFIGURE, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Provides a decision for a pending tool permission request. /// /// Wire method: `session.permissions.handlePendingPermissionRequest`. @@ -5158,6 +7054,7 @@ impl<'a> SessionRpcPermissions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn handle_pending_permission_request( &self, params: PermissionDecisionRequest, @@ -5175,6 +7072,25 @@ impl<'a> SessionRpcPermissions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn handle_pending_permission_request( + &self, + params: PermissionDecisionRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_PERMISSIONS_HANDLEPENDINGPERMISSIONREQUEST, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Reconstructs the set of pending tool permission requests from the session's event history. /// /// Wire method: `session.permissions.pendingRequests`. @@ -5190,6 +7106,7 @@ impl<'a> SessionRpcPermissions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn pending_requests(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -5203,6 +7120,21 @@ impl<'a> SessionRpcPermissions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn pending_requests(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_PERMISSIONS_PENDINGREQUESTS, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Enables or disables automatic approval of tool permission requests for the session. /// /// Wire method: `session.permissions.setApproveAll`. @@ -5222,6 +7154,7 @@ impl<'a> SessionRpcPermissions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn set_approve_all( &self, params: PermissionsSetApproveAllRequest, @@ -5239,6 +7172,25 @@ impl<'a> SessionRpcPermissions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn set_approve_all( + &self, + params: PermissionsSetApproveAllRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_PERMISSIONS_SETAPPROVEALL, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Enables or disables full allow-all permissions (tools, paths, and URLs) for the session. Used by attach-mode clients (e.g. LocalRpcSession's `/allow-all` forwarder) to flip the target session's permission state. Unlike `setApproveAll`, this swaps in the unrestricted path and URL managers and emits `session.permissions_changed` on transition. The result returns the authoritative post-mutation state so callers can update their local mirrors without racing the `session.permissions_changed` notification on the same wire. /// /// Wire method: `session.permissions.setAllowAll`. @@ -5258,6 +7210,7 @@ impl<'a> SessionRpcPermissions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn set_allow_all( &self, params: PermissionsSetAllowAllRequest, @@ -5275,6 +7228,25 @@ impl<'a> SessionRpcPermissions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn set_allow_all( + &self, + params: PermissionsSetAllowAllRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_PERMISSIONS_SETALLOWALL, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Returns whether full allow-all permissions are currently active for the session. /// /// Wire method: `session.permissions.getAllowAll`. @@ -5290,6 +7262,7 @@ impl<'a> SessionRpcPermissions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn get_allow_all(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -5303,6 +7276,21 @@ impl<'a> SessionRpcPermissions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn get_allow_all(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_PERMISSIONS_GETALLOWALL, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Adds or removes session-scoped or location-scoped permission rules. /// /// Wire method: `session.permissions.modifyRules`. @@ -5322,6 +7310,7 @@ impl<'a> SessionRpcPermissions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn modify_rules( &self, params: PermissionsModifyRulesParams, @@ -5339,6 +7328,25 @@ impl<'a> SessionRpcPermissions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn modify_rules( + &self, + params: PermissionsModifyRulesParams, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_PERMISSIONS_MODIFYRULES, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Sets whether the client wants permission prompts bridged into session events. /// /// Wire method: `session.permissions.setRequired`. @@ -5358,6 +7366,7 @@ impl<'a> SessionRpcPermissions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn set_required( &self, params: PermissionsSetRequiredRequest, @@ -5375,7 +7384,26 @@ impl<'a> SessionRpcPermissions<'a> { Ok(serde_json::from_value(_value)?) } - /// Clears session-scoped tool permission approvals. + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn set_required( + &self, + params: PermissionsSetRequiredRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_PERMISSIONS_SETREQUIRED, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + + /// Clears session-scoped tool permission approvals. /// /// Wire method: `session.permissions.resetSessionApprovals`. /// @@ -5390,6 +7418,7 @@ impl<'a> SessionRpcPermissions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn reset_session_approvals( &self, ) -> Result { @@ -5405,6 +7434,23 @@ impl<'a> SessionRpcPermissions<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn reset_session_approvals( + &self, + ) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_PERMISSIONS_RESETSESSIONAPPROVALS, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Notifies the runtime that a permission prompt UI has been shown to the user. /// /// Wire method: `session.permissions.notifyPromptShown`. @@ -5424,6 +7470,7 @@ impl<'a> SessionRpcPermissions<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn notify_prompt_shown( &self, params: PermissionPromptShownNotification, @@ -5440,6 +7487,25 @@ impl<'a> SessionRpcPermissions<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn notify_prompt_shown( + &self, + params: PermissionPromptShownNotification, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_PERMISSIONS_NOTIFYPROMPTSHOWN, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `session.permissions.folderTrust.*` RPCs. @@ -5468,6 +7534,7 @@ impl<'a> SessionRpcPermissionsFolderTrust<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn is_trusted( &self, params: FolderTrustCheckParams, @@ -5485,6 +7552,25 @@ impl<'a> SessionRpcPermissionsFolderTrust<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn is_trusted( + &self, + params: FolderTrustCheckParams, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_PERMISSIONS_FOLDERTRUST_ISTRUSTED, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Adds a folder to the user's trusted folders list. /// /// Wire method: `session.permissions.folderTrust.addTrusted`. @@ -5504,6 +7590,7 @@ impl<'a> SessionRpcPermissionsFolderTrust<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn add_trusted( &self, params: FolderTrustAddParams, @@ -5520,6 +7607,25 @@ impl<'a> SessionRpcPermissionsFolderTrust<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn add_trusted( + &self, + params: FolderTrustAddParams, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_PERMISSIONS_FOLDERTRUST_ADDTRUSTED, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `session.permissions.locations.*` RPCs. @@ -5548,6 +7654,7 @@ impl<'a> SessionRpcPermissionsLocations<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn resolve( &self, params: PermissionLocationResolveParams, @@ -5565,6 +7672,25 @@ impl<'a> SessionRpcPermissionsLocations<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn resolve( + &self, + params: PermissionLocationResolveParams, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_PERMISSIONS_LOCATIONS_RESOLVE, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Applies persisted location-scoped tool approvals and allowed directories for a working directory to this session's permission service. /// /// Wire method: `session.permissions.locations.apply`. @@ -5584,6 +7710,7 @@ impl<'a> SessionRpcPermissionsLocations<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn apply( &self, params: PermissionLocationApplyParams, @@ -5601,6 +7728,25 @@ impl<'a> SessionRpcPermissionsLocations<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn apply( + &self, + params: PermissionLocationApplyParams, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_PERMISSIONS_LOCATIONS_APPLY, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Persists a tool approval for a permission location and applies its rules to this session's live permission service. /// /// Wire method: `session.permissions.locations.addToolApproval`. @@ -5620,6 +7766,7 @@ impl<'a> SessionRpcPermissionsLocations<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn add_tool_approval( &self, params: PermissionLocationAddToolApprovalParams, @@ -5636,6 +7783,25 @@ impl<'a> SessionRpcPermissionsLocations<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn add_tool_approval( + &self, + params: PermissionLocationAddToolApprovalParams, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_PERMISSIONS_LOCATIONS_ADDTOOLAPPROVAL, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `session.permissions.paths.*` RPCs. @@ -5660,6 +7826,7 @@ impl<'a> SessionRpcPermissionsPaths<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn list(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -5673,6 +7840,21 @@ impl<'a> SessionRpcPermissionsPaths<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn list(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_PERMISSIONS_PATHS_LIST, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Adds a directory to the session's allow-list. /// /// Wire method: `session.permissions.paths.add`. @@ -5692,6 +7874,7 @@ impl<'a> SessionRpcPermissionsPaths<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn add( &self, params: PermissionPathsAddParams, @@ -5709,6 +7892,25 @@ impl<'a> SessionRpcPermissionsPaths<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn add( + &self, + params: PermissionPathsAddParams, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_PERMISSIONS_PATHS_ADD, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Updates the session's primary working directory used by the permission policy. /// /// Wire method: `session.permissions.paths.updatePrimary`. @@ -5728,6 +7930,7 @@ impl<'a> SessionRpcPermissionsPaths<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn update_primary( &self, params: PermissionPathsUpdatePrimaryParams, @@ -5745,6 +7948,25 @@ impl<'a> SessionRpcPermissionsPaths<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn update_primary( + &self, + params: PermissionPathsUpdatePrimaryParams, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_PERMISSIONS_PATHS_UPDATEPRIMARY, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Reports whether a path falls within any of the session's allowed directories. /// /// Wire method: `session.permissions.paths.isPathWithinAllowedDirectories`. @@ -5764,6 +7986,7 @@ impl<'a> SessionRpcPermissionsPaths<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn is_path_within_allowed_directories( &self, params: PermissionPathsAllowedCheckParams, @@ -5781,6 +8004,25 @@ impl<'a> SessionRpcPermissionsPaths<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn is_path_within_allowed_directories( + &self, + params: PermissionPathsAllowedCheckParams, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_PERMISSIONS_PATHS_ISPATHWITHINALLOWEDDIRECTORIES, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Reports whether a path falls within the session's workspace (primary) directory. /// /// Wire method: `session.permissions.paths.isPathWithinWorkspace`. @@ -5800,6 +8042,7 @@ impl<'a> SessionRpcPermissionsPaths<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn is_path_within_workspace( &self, params: PermissionPathsWorkspaceCheckParams, @@ -5816,6 +8059,25 @@ impl<'a> SessionRpcPermissionsPaths<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn is_path_within_workspace( + &self, + params: PermissionPathsWorkspaceCheckParams, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_PERMISSIONS_PATHS_ISPATHWITHINWORKSPACE, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `session.permissions.urls.*` RPCs. @@ -5844,6 +8106,7 @@ impl<'a> SessionRpcPermissionsUrls<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn set_unrestricted_mode( &self, params: PermissionUrlsSetUnrestrictedModeParams, @@ -5860,6 +8123,25 @@ impl<'a> SessionRpcPermissionsUrls<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn set_unrestricted_mode( + &self, + params: PermissionUrlsSetUnrestrictedModeParams, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_PERMISSIONS_URLS_SETUNRESTRICTEDMODE, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `session.plan.*` RPCs. @@ -5884,6 +8166,7 @@ impl<'a> SessionRpcPlan<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn read(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -5894,6 +8177,18 @@ impl<'a> SessionRpcPlan<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn read(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_PLAN_READ, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Writes new content to the session plan file. /// /// Wire method: `session.plan.update`. @@ -5909,6 +8204,7 @@ impl<'a> SessionRpcPlan<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn update(&self, params: PlanUpdateRequest) -> Result<(), Error> { let mut wire_params = serde_json::to_value(params)?; wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); @@ -5920,6 +8216,19 @@ impl<'a> SessionRpcPlan<'a> { Ok(()) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn update(&self, params: PlanUpdateRequest) -> Result<(), Error> { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_PLAN_UPDATE, Some(wire_params)) + .await?; + Ok(()) + } + /// Deletes the session plan file from the workspace. /// /// Wire method: `session.plan.delete`. @@ -5931,6 +8240,7 @@ impl<'a> SessionRpcPlan<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn delete(&self) -> Result<(), Error> { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -5941,6 +8251,18 @@ impl<'a> SessionRpcPlan<'a> { Ok(()) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn delete(&self) -> Result<(), Error> { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_PLAN_DELETE, Some(wire_params)) + .await?; + Ok(()) + } + /// Reads todo rows from the session SQL database for plan rendering. /// /// Wire method: `session.plan.readSqlTodos`. @@ -5956,6 +8278,7 @@ impl<'a> SessionRpcPlan<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn read_sql_todos(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -5966,6 +8289,18 @@ impl<'a> SessionRpcPlan<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn read_sql_todos(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_PLAN_READSQLTODOS, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Reads todo rows AND dependency edges from the session SQL database for structured progress UI. Same defensive behavior as readSqlTodos — returns empty arrays when the database, tables, or columns aren't available. Clients should call this on session start and after every `session.todos_changed` event to refresh structured-UI rendering. /// /// Wire method: `session.plan.readSqlTodosWithDependencies`. @@ -5981,6 +8316,7 @@ impl<'a> SessionRpcPlan<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn read_sql_todos_with_dependencies( &self, ) -> Result { @@ -5995,6 +8331,23 @@ impl<'a> SessionRpcPlan<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn read_sql_todos_with_dependencies( + &self, + ) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_PLAN_READSQLTODOSWITHDEPENDENCIES, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `session.plugins.*` RPCs. @@ -6019,6 +8372,7 @@ impl<'a> SessionRpcPlugins<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn list(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -6029,6 +8383,18 @@ impl<'a> SessionRpcPlugins<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn list(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_PLUGINS_LIST, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Reloads the session's plugin set, refreshing MCP servers, custom agents, hooks, and skills cache so SDK-driven changes via `server.plugins.*` take effect immediately. /// /// Wire method: `session.plugins.reload`. @@ -6040,6 +8406,7 @@ impl<'a> SessionRpcPlugins<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn reload(&self) -> Result<(), Error> { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -6050,6 +8417,18 @@ impl<'a> SessionRpcPlugins<'a> { Ok(()) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn reload(&self) -> Result<(), Error> { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_PLUGINS_RELOAD, Some(wire_params)) + .await?; + Ok(()) + } + /// Reloads the session's plugin set, refreshing MCP servers, custom agents, hooks, and skills cache so SDK-driven changes via `server.plugins.*` take effect immediately. /// /// Wire method: `session.plugins.reload`. @@ -6065,6 +8444,7 @@ impl<'a> SessionRpcPlugins<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn reload_with_params(&self, params: PluginsReloadRequest) -> Result<(), Error> { let mut wire_params = serde_json::to_value(params)?; wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); @@ -6075,6 +8455,22 @@ impl<'a> SessionRpcPlugins<'a> { .await?; Ok(()) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn reload_with_params( + &self, + params: PluginsReloadRequest, + ) -> Result<(), Error> { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_PLUGINS_RELOAD, Some(wire_params)) + .await?; + Ok(()) + } } /// `session.provider.*` RPCs. @@ -6099,6 +8495,7 @@ impl<'a> SessionRpcProvider<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn get_endpoint(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -6109,6 +8506,18 @@ impl<'a> SessionRpcProvider<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn get_endpoint(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_PROVIDER_GETENDPOINT, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Returns the provider endpoint and credentials the session is currently configured to talk to, so the caller can make inference calls directly against the same backend the session uses. /// /// Wire method: `session.provider.getEndpoint`. @@ -6128,6 +8537,7 @@ impl<'a> SessionRpcProvider<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn get_endpoint_with_params( &self, params: ProviderGetEndpointRequest, @@ -6141,6 +8551,22 @@ impl<'a> SessionRpcProvider<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn get_endpoint_with_params( + &self, + params: ProviderGetEndpointRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_PROVIDER_GETENDPOINT, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `session.queue.*` RPCs. @@ -6165,6 +8591,7 @@ impl<'a> SessionRpcQueue<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn pending_items(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -6175,6 +8602,18 @@ impl<'a> SessionRpcQueue<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn pending_items(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_QUEUE_PENDINGITEMS, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Removes the most recently queued user-facing item (LIFO). /// /// Wire method: `session.queue.removeMostRecent`. @@ -6190,6 +8629,7 @@ impl<'a> SessionRpcQueue<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn remove_most_recent(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -6203,6 +8643,21 @@ impl<'a> SessionRpcQueue<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn remove_most_recent(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_QUEUE_REMOVEMOSTRECENT, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Clears all pending queued items on the local session. /// /// Wire method: `session.queue.clear`. @@ -6214,6 +8669,7 @@ impl<'a> SessionRpcQueue<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn clear(&self) -> Result<(), Error> { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -6223,6 +8679,18 @@ impl<'a> SessionRpcQueue<'a> { .await?; Ok(()) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn clear(&self) -> Result<(), Error> { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_QUEUE_CLEAR, Some(wire_params)) + .await?; + Ok(()) + } } /// `session.remote.*` RPCs. @@ -6251,6 +8719,7 @@ impl<'a> SessionRpcRemote<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn enable(&self, params: RemoteEnableRequest) -> Result { let mut wire_params = serde_json::to_value(params)?; wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); @@ -6262,6 +8731,22 @@ impl<'a> SessionRpcRemote<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn enable( + &self, + params: RemoteEnableRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_REMOTE_ENABLE, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Disables remote session export and steering. /// /// Wire method: `session.remote.disable`. @@ -6273,6 +8758,7 @@ impl<'a> SessionRpcRemote<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn disable(&self) -> Result<(), Error> { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -6283,6 +8769,18 @@ impl<'a> SessionRpcRemote<'a> { Ok(()) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn disable(&self) -> Result<(), Error> { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_REMOTE_DISABLE, Some(wire_params)) + .await?; + Ok(()) + } + /// Persists a remote-steerability change emitted by the host as a session event. /// /// Wire method: `session.remote.notifySteerableChanged`. @@ -6302,6 +8800,7 @@ impl<'a> SessionRpcRemote<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn notify_steerable_changed( &self, params: RemoteNotifySteerableChangedRequest, @@ -6318,6 +8817,25 @@ impl<'a> SessionRpcRemote<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn notify_steerable_changed( + &self, + params: RemoteNotifySteerableChangedRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_REMOTE_NOTIFYSTEERABLECHANGED, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `session.schedule.*` RPCs. @@ -6342,6 +8860,7 @@ impl<'a> SessionRpcSchedule<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn list(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -6352,6 +8871,18 @@ impl<'a> SessionRpcSchedule<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn list(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_SCHEDULE_LIST, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Removes a scheduled prompt by id. /// /// Wire method: `session.schedule.stop`. @@ -6371,6 +8902,7 @@ impl<'a> SessionRpcSchedule<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn stop(&self, params: ScheduleStopRequest) -> Result { let mut wire_params = serde_json::to_value(params)?; wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); @@ -6381,6 +8913,22 @@ impl<'a> SessionRpcSchedule<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn stop( + &self, + params: ScheduleStopRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_SCHEDULE_STOP, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `session.shell.*` RPCs. @@ -6409,6 +8957,7 @@ impl<'a> SessionRpcShell<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn exec(&self, params: ShellExecRequest) -> Result { let mut wire_params = serde_json::to_value(params)?; wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); @@ -6420,6 +8969,19 @@ impl<'a> SessionRpcShell<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn exec(&self, params: ShellExecRequest) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_SHELL_EXEC, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Sends a signal to a shell process previously started via "shell.exec". /// /// Wire method: `session.shell.kill`. @@ -6439,6 +9001,7 @@ impl<'a> SessionRpcShell<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn kill(&self, params: ShellKillRequest) -> Result { let mut wire_params = serde_json::to_value(params)?; wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); @@ -6450,6 +9013,19 @@ impl<'a> SessionRpcShell<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn kill(&self, params: ShellKillRequest) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_SHELL_KILL, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Executes a user-requested shell command through the session runtime. /// /// Wire method: `session.shell.executeUserRequested`. @@ -6469,6 +9045,7 @@ impl<'a> SessionRpcShell<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn execute_user_requested( &self, params: ShellExecuteUserRequestedRequest, @@ -6486,6 +9063,25 @@ impl<'a> SessionRpcShell<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn execute_user_requested( + &self, + params: ShellExecuteUserRequestedRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_SHELL_EXECUTEUSERREQUESTED, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Cancels a user-requested shell command by request ID. /// /// Wire method: `session.shell.cancelUserRequested`. @@ -6505,6 +9101,7 @@ impl<'a> SessionRpcShell<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn cancel_user_requested( &self, params: ShellCancelUserRequestedRequest, @@ -6521,6 +9118,25 @@ impl<'a> SessionRpcShell<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn cancel_user_requested( + &self, + params: ShellCancelUserRequestedRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_SHELL_CANCELUSERREQUESTED, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `session.skills.*` RPCs. @@ -6545,6 +9161,7 @@ impl<'a> SessionRpcSkills<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn list(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -6555,6 +9172,18 @@ impl<'a> SessionRpcSkills<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn list(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_SKILLS_LIST, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Returns the skills that have been invoked during this session. /// /// Wire method: `session.skills.getInvoked`. @@ -6570,6 +9199,7 @@ impl<'a> SessionRpcSkills<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn get_invoked(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -6580,6 +9210,18 @@ impl<'a> SessionRpcSkills<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn get_invoked(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_SKILLS_GETINVOKED, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Enables a skill for the session. /// /// Wire method: `session.skills.enable`. @@ -6595,6 +9237,7 @@ impl<'a> SessionRpcSkills<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn enable(&self, params: SkillsEnableRequest) -> Result<(), Error> { let mut wire_params = serde_json::to_value(params)?; wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); @@ -6606,6 +9249,19 @@ impl<'a> SessionRpcSkills<'a> { Ok(()) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn enable(&self, params: SkillsEnableRequest) -> Result<(), Error> { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_SKILLS_ENABLE, Some(wire_params)) + .await?; + Ok(()) + } + /// Disables a skill for the session. /// /// Wire method: `session.skills.disable`. @@ -6621,6 +9277,7 @@ impl<'a> SessionRpcSkills<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn disable(&self, params: SkillsDisableRequest) -> Result<(), Error> { let mut wire_params = serde_json::to_value(params)?; wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); @@ -6632,6 +9289,19 @@ impl<'a> SessionRpcSkills<'a> { Ok(()) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn disable(&self, params: SkillsDisableRequest) -> Result<(), Error> { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_SKILLS_DISABLE, Some(wire_params)) + .await?; + Ok(()) + } + /// Reloads skill definitions for the session. /// /// Wire method: `session.skills.reload`. @@ -6647,6 +9317,7 @@ impl<'a> SessionRpcSkills<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn reload(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -6657,6 +9328,18 @@ impl<'a> SessionRpcSkills<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn reload(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_SKILLS_RELOAD, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Ensures the session's skill definitions have been loaded from disk. /// /// Wire method: `session.skills.ensureLoaded`. @@ -6668,6 +9351,7 @@ impl<'a> SessionRpcSkills<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn ensure_loaded(&self) -> Result<(), Error> { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -6677,12 +9361,24 @@ impl<'a> SessionRpcSkills<'a> { .await?; Ok(()) } -} -/// `session.tasks.*` RPCs. -#[derive(Clone, Copy)] -pub struct SessionRpcTasks<'a> { - pub(crate) session: &'a Session, + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn ensure_loaded(&self) -> Result<(), Error> { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_SKILLS_ENSURELOADED, Some(wire_params)) + .await?; + Ok(()) + } +} + +/// `session.tasks.*` RPCs. +#[derive(Clone, Copy)] +pub struct SessionRpcTasks<'a> { + pub(crate) session: &'a Session, } impl<'a> SessionRpcTasks<'a> { @@ -6705,6 +9401,7 @@ impl<'a> SessionRpcTasks<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn start_agent( &self, params: TasksStartAgentRequest, @@ -6719,6 +9416,22 @@ impl<'a> SessionRpcTasks<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn start_agent( + &self, + params: TasksStartAgentRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_TASKS_STARTAGENT, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Lists background tasks tracked by the session. /// /// Wire method: `session.tasks.list`. @@ -6734,6 +9447,7 @@ impl<'a> SessionRpcTasks<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn list(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -6744,6 +9458,18 @@ impl<'a> SessionRpcTasks<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn list(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_TASKS_LIST, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Refreshes metadata for any detached background shells the runtime knows about. /// /// Wire method: `session.tasks.refresh`. @@ -6759,6 +9485,7 @@ impl<'a> SessionRpcTasks<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn refresh(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -6769,6 +9496,18 @@ impl<'a> SessionRpcTasks<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn refresh(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_TASKS_REFRESH, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Waits for all in-flight background tasks and any follow-up turns to settle. /// /// Wire method: `session.tasks.waitForPending`. @@ -6784,6 +9523,7 @@ impl<'a> SessionRpcTasks<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn wait_for_pending(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -6794,6 +9534,18 @@ impl<'a> SessionRpcTasks<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn wait_for_pending(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_TASKS_WAITFORPENDING, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Returns progress information for a background task by ID. /// /// Wire method: `session.tasks.getProgress`. @@ -6813,6 +9565,7 @@ impl<'a> SessionRpcTasks<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn get_progress( &self, params: TasksGetProgressRequest, @@ -6827,6 +9580,22 @@ impl<'a> SessionRpcTasks<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn get_progress( + &self, + params: TasksGetProgressRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_TASKS_GETPROGRESS, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Returns the first sync-waiting task that can currently be promoted to background mode. /// /// Wire method: `session.tasks.getCurrentPromotable`. @@ -6842,6 +9611,7 @@ impl<'a> SessionRpcTasks<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn get_current_promotable(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -6855,6 +9625,23 @@ impl<'a> SessionRpcTasks<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn get_current_promotable( + &self, + ) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_TASKS_GETCURRENTPROMOTABLE, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Promotes an eligible synchronously-waited task so it continues running in the background. /// /// Wire method: `session.tasks.promoteToBackground`. @@ -6874,6 +9661,7 @@ impl<'a> SessionRpcTasks<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn promote_to_background( &self, params: TasksPromoteToBackgroundRequest, @@ -6891,6 +9679,25 @@ impl<'a> SessionRpcTasks<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn promote_to_background( + &self, + params: TasksPromoteToBackgroundRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_TASKS_PROMOTETOBACKGROUND, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Atomically promotes the first promotable sync-waiting task to background mode and returns it. /// /// Wire method: `session.tasks.promoteCurrentToBackground`. @@ -6906,6 +9713,7 @@ impl<'a> SessionRpcTasks<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn promote_current_to_background( &self, ) -> Result { @@ -6921,6 +9729,23 @@ impl<'a> SessionRpcTasks<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn promote_current_to_background( + &self, + ) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_TASKS_PROMOTECURRENTTOBACKGROUND, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Cancels a background task. /// /// Wire method: `session.tasks.cancel`. @@ -6940,6 +9765,7 @@ impl<'a> SessionRpcTasks<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn cancel(&self, params: TasksCancelRequest) -> Result { let mut wire_params = serde_json::to_value(params)?; wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); @@ -6951,6 +9777,22 @@ impl<'a> SessionRpcTasks<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn cancel( + &self, + params: TasksCancelRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_TASKS_CANCEL, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Removes a completed or cancelled background task from tracking. /// /// Wire method: `session.tasks.remove`. @@ -6970,6 +9812,7 @@ impl<'a> SessionRpcTasks<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn remove(&self, params: TasksRemoveRequest) -> Result { let mut wire_params = serde_json::to_value(params)?; wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); @@ -6981,6 +9824,22 @@ impl<'a> SessionRpcTasks<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn remove( + &self, + params: TasksRemoveRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_TASKS_REMOVE, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Sends a message to a background agent task. /// /// Wire method: `session.tasks.sendMessage`. @@ -7000,6 +9859,7 @@ impl<'a> SessionRpcTasks<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn send_message( &self, params: TasksSendMessageRequest, @@ -7013,6 +9873,22 @@ impl<'a> SessionRpcTasks<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn send_message( + &self, + params: TasksSendMessageRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_TASKS_SENDMESSAGE, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `session.telemetry.*` RPCs. @@ -7037,6 +9913,7 @@ impl<'a> SessionRpcTelemetry<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn get_engagement_id(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -7050,6 +9927,21 @@ impl<'a> SessionRpcTelemetry<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn get_engagement_id(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_TELEMETRY_GETENGAGEMENTID, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Sets feature override key/value pairs to attach to subsequent telemetry events for the session. /// /// Wire method: `session.telemetry.setFeatureOverrides`. @@ -7065,6 +9957,7 @@ impl<'a> SessionRpcTelemetry<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn set_feature_overrides( &self, params: TelemetrySetFeatureOverridesRequest, @@ -7081,6 +9974,25 @@ impl<'a> SessionRpcTelemetry<'a> { .await?; Ok(()) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn set_feature_overrides( + &self, + params: TelemetrySetFeatureOverridesRequest, + ) -> Result<(), Error> { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_TELEMETRY_SETFEATUREOVERRIDES, + Some(wire_params), + ) + .await?; + Ok(()) + } } /// `session.tools.*` RPCs. @@ -7109,6 +10021,7 @@ impl<'a> SessionRpcTools<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn handle_pending_tool_call( &self, params: HandlePendingToolCallRequest, @@ -7126,6 +10039,25 @@ impl<'a> SessionRpcTools<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn handle_pending_tool_call( + &self, + params: HandlePendingToolCallRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_TOOLS_HANDLEPENDINGTOOLCALL, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Resolves, builds, and validates the runtime tool list for the session. /// /// Wire method: `session.tools.initializeAndValidate`. @@ -7141,6 +10073,7 @@ impl<'a> SessionRpcTools<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn initialize_and_validate(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -7154,6 +10087,23 @@ impl<'a> SessionRpcTools<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn initialize_and_validate( + &self, + ) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_TOOLS_INITIALIZEANDVALIDATE, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Returns lightweight metadata for the session's currently initialized tools. /// /// Wire method: `session.tools.getCurrentMetadata`. @@ -7169,6 +10119,7 @@ impl<'a> SessionRpcTools<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn get_current_metadata(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -7182,6 +10133,23 @@ impl<'a> SessionRpcTools<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn get_current_metadata( + &self, + ) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_TOOLS_GETCURRENTMETADATA, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Updates the current session's live subagent settings after user settings change. The persisted user settings remain the source of truth for future sessions. /// /// Wire method: `session.tools.updateSubagentSettings`. @@ -7201,6 +10169,7 @@ impl<'a> SessionRpcTools<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn update_subagent_settings( &self, params: UpdateSubagentSettingsRequest, @@ -7217,6 +10186,25 @@ impl<'a> SessionRpcTools<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn update_subagent_settings( + &self, + params: UpdateSubagentSettingsRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_TOOLS_UPDATESUBAGENTSETTINGS, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `session.ui.*` RPCs. @@ -7245,6 +10233,7 @@ impl<'a> SessionRpcUi<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn ephemeral_query( &self, params: UIEphemeralQueryRequest, @@ -7259,6 +10248,22 @@ impl<'a> SessionRpcUi<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn ephemeral_query( + &self, + params: UIEphemeralQueryRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_UI_EPHEMERALQUERY, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Requests structured input from a UI-capable client. /// /// Wire method: `session.ui.elicitation`. @@ -7278,6 +10283,7 @@ impl<'a> SessionRpcUi<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn elicitation( &self, params: UIElicitationRequest, @@ -7292,6 +10298,22 @@ impl<'a> SessionRpcUi<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn elicitation( + &self, + params: UIElicitationRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_UI_ELICITATION, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Provides the user response for a pending elicitation request. /// /// Wire method: `session.ui.handlePendingElicitation`. @@ -7311,6 +10333,7 @@ impl<'a> SessionRpcUi<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn handle_pending_elicitation( &self, params: UIHandlePendingElicitationRequest, @@ -7328,6 +10351,25 @@ impl<'a> SessionRpcUi<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn handle_pending_elicitation( + &self, + params: UIHandlePendingElicitationRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_UI_HANDLEPENDINGELICITATION, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Resolves a pending `user_input.requested` event with the user's response. /// /// Wire method: `session.ui.handlePendingUserInput`. @@ -7347,6 +10389,7 @@ impl<'a> SessionRpcUi<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn handle_pending_user_input( &self, params: UIHandlePendingUserInputRequest, @@ -7364,6 +10407,25 @@ impl<'a> SessionRpcUi<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn handle_pending_user_input( + &self, + params: UIHandlePendingUserInputRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_UI_HANDLEPENDINGUSERINPUT, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Resolves a pending `sampling.requested` event with a sampling result, or rejects it. /// /// Wire method: `session.ui.handlePendingSampling`. @@ -7383,6 +10445,7 @@ impl<'a> SessionRpcUi<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn handle_pending_sampling( &self, params: UIHandlePendingSamplingRequest, @@ -7400,6 +10463,25 @@ impl<'a> SessionRpcUi<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn handle_pending_sampling( + &self, + params: UIHandlePendingSamplingRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_UI_HANDLEPENDINGSAMPLING, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Resolves a pending `auto_mode_switch.requested` event with the user's accept/decline decision. /// /// Wire method: `session.ui.handlePendingAutoModeSwitch`. @@ -7419,6 +10501,7 @@ impl<'a> SessionRpcUi<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn handle_pending_auto_mode_switch( &self, params: UIHandlePendingAutoModeSwitchRequest, @@ -7436,6 +10519,25 @@ impl<'a> SessionRpcUi<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn handle_pending_auto_mode_switch( + &self, + params: UIHandlePendingAutoModeSwitchRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_UI_HANDLEPENDINGAUTOMODESWITCH, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Resolves a pending `exit_plan_mode.requested` event with the user's response. /// /// Wire method: `session.ui.handlePendingExitPlanMode`. @@ -7455,6 +10557,7 @@ impl<'a> SessionRpcUi<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn handle_pending_exit_plan_mode( &self, params: UIHandlePendingExitPlanModeRequest, @@ -7472,6 +10575,25 @@ impl<'a> SessionRpcUi<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn handle_pending_exit_plan_mode( + &self, + params: UIHandlePendingExitPlanModeRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_UI_HANDLEPENDINGEXITPLANMODE, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Registers an in-process handler for auto-mode-switch requests so the server bridge skips dispatch. /// /// Wire method: `session.ui.registerDirectAutoModeSwitchHandler`. @@ -7487,6 +10609,7 @@ impl<'a> SessionRpcUi<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn register_direct_auto_mode_switch_handler( &self, ) -> Result { @@ -7502,6 +10625,23 @@ impl<'a> SessionRpcUi<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn register_direct_auto_mode_switch_handler( + &self, + ) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_UI_REGISTERDIRECTAUTOMODESWITCHHANDLER, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Unregisters a previously-registered in-process auto-mode-switch handler by its opaque handle. /// /// Wire method: `session.ui.unregisterDirectAutoModeSwitchHandler`. @@ -7521,6 +10661,7 @@ impl<'a> SessionRpcUi<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn unregister_direct_auto_mode_switch_handler( &self, params: UIUnregisterDirectAutoModeSwitchHandlerRequest, @@ -7537,6 +10678,25 @@ impl<'a> SessionRpcUi<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn unregister_direct_auto_mode_switch_handler( + &self, + params: UIUnregisterDirectAutoModeSwitchHandlerRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_UI_UNREGISTERDIRECTAUTOMODESWITCHHANDLER, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `session.usage.*` RPCs. @@ -7561,6 +10721,7 @@ impl<'a> SessionRpcUsage<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn get_metrics(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -7570,6 +10731,18 @@ impl<'a> SessionRpcUsage<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn get_metrics(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_USAGE_GETMETRICS, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } } /// `session.workspaces.*` RPCs. @@ -7594,6 +10767,7 @@ impl<'a> SessionRpcWorkspaces<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn get_workspace(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -7607,6 +10781,21 @@ impl<'a> SessionRpcWorkspaces<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn get_workspace(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_WORKSPACES_GETWORKSPACE, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Lists files stored in the session workspace files directory. /// /// Wire method: `session.workspaces.listFiles`. @@ -7622,6 +10811,7 @@ impl<'a> SessionRpcWorkspaces<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn list_files(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -7632,6 +10822,18 @@ impl<'a> SessionRpcWorkspaces<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn list_files(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_WORKSPACES_LISTFILES, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Reads a file from the session workspace files directory. /// /// Wire method: `session.workspaces.readFile`. @@ -7651,6 +10853,7 @@ impl<'a> SessionRpcWorkspaces<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn read_file( &self, params: WorkspacesReadFileRequest, @@ -7665,6 +10868,22 @@ impl<'a> SessionRpcWorkspaces<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn read_file( + &self, + params: WorkspacesReadFileRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_WORKSPACES_READFILE, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Creates or overwrites a file in the session workspace files directory. /// /// Wire method: `session.workspaces.createFile`. @@ -7680,6 +10899,7 @@ impl<'a> SessionRpcWorkspaces<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn create_file(&self, params: WorkspacesCreateFileRequest) -> Result<(), Error> { let mut wire_params = serde_json::to_value(params)?; wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); @@ -7694,6 +10914,25 @@ impl<'a> SessionRpcWorkspaces<'a> { Ok(()) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn create_file( + &self, + params: WorkspacesCreateFileRequest, + ) -> Result<(), Error> { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_WORKSPACES_CREATEFILE, + Some(wire_params), + ) + .await?; + Ok(()) + } + /// Lists workspace checkpoints in chronological order. /// /// Wire method: `session.workspaces.listCheckpoints`. @@ -7709,6 +10948,7 @@ impl<'a> SessionRpcWorkspaces<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn list_checkpoints(&self) -> Result { let wire_params = serde_json::json!({ "sessionId": self.session.id() }); let _value = self @@ -7722,6 +10962,21 @@ impl<'a> SessionRpcWorkspaces<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn list_checkpoints(&self) -> Result { + let wire_params = serde_json::json!({ "sessionId": self.session.id() }); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_WORKSPACES_LISTCHECKPOINTS, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Reads the content of a workspace checkpoint by number. /// /// Wire method: `session.workspaces.readCheckpoint`. @@ -7741,6 +10996,7 @@ impl<'a> SessionRpcWorkspaces<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn read_checkpoint( &self, params: WorkspacesReadCheckpointRequest, @@ -7758,6 +11014,25 @@ impl<'a> SessionRpcWorkspaces<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn read_checkpoint( + &self, + params: WorkspacesReadCheckpointRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_WORKSPACES_READCHECKPOINT, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Saves pasted content as a UTF-8 file in the session workspace. /// /// Wire method: `session.workspaces.saveLargePaste`. @@ -7777,6 +11052,7 @@ impl<'a> SessionRpcWorkspaces<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn save_large_paste( &self, params: WorkspacesSaveLargePasteRequest, @@ -7794,6 +11070,25 @@ impl<'a> SessionRpcWorkspaces<'a> { Ok(serde_json::from_value(_value)?) } + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn save_large_paste( + &self, + params: WorkspacesSaveLargePasteRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call( + rpc_methods::SESSION_WORKSPACES_SAVELARGEPASTE, + Some(wire_params), + ) + .await?; + Ok(serde_json::from_value(_value)?) + } + /// Computes a diff for the session workspace. /// /// Wire method: `session.workspaces.diff`. @@ -7813,6 +11108,7 @@ impl<'a> SessionRpcWorkspaces<'a> { /// SDK and CLI versions if your code depends on it. /// /// + #[cfg(feature = "experimental")] pub async fn diff(&self, params: WorkspacesDiffRequest) -> Result { let mut wire_params = serde_json::to_value(params)?; wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); @@ -7823,4 +11119,20 @@ impl<'a> SessionRpcWorkspaces<'a> { .await?; Ok(serde_json::from_value(_value)?) } + + #[cfg(not(feature = "experimental"))] + #[doc(hidden)] + pub(crate) async fn diff( + &self, + params: WorkspacesDiffRequest, + ) -> Result { + let mut wire_params = serde_json::to_value(params)?; + wire_params["sessionId"] = serde_json::Value::String(self.session.id().to_string()); + let _value = self + .session + .client() + .call(rpc_methods::SESSION_WORKSPACES_DIFF, Some(wire_params)) + .await?; + Ok(serde_json::from_value(_value)?) + } } diff --git a/scripts/codegen/python.ts b/scripts/codegen/python.ts index 783ac8244..62a33c6b8 100644 --- a/scripts/codegen/python.ts +++ b/scripts/codegen/python.ts @@ -3131,6 +3131,8 @@ from uuid import UUID import dateutil.parser +from ..experimental import experimental + T = TypeVar("T") EnumT = TypeVar("EnumT", bound=Enum) @@ -3507,6 +3509,15 @@ function emitMethod(lines: string[], name: string, method: RpcMethod, isSession: : ` async def ${methodName}(self, params: ${paramsType}, *, timeout: float | None = None) -> ${resultType}:` : ` async def ${methodName}(self, *, timeout: float | None = None) -> ${resultType}:`; + // Emit the runtime `@experimental` decorator so that calling an experimental + // method raises an `ExperimentalWarning` the consumer must explicitly silence + // (the Python analog of C# `[Experimental]` / Java `@CopilotExperimental`). + // Internal methods already carry no stability guarantee via the `_` prefix, so + // they are left undecorated to avoid noise from internal SDK call sites. + const isExperimentalMethod = (method.stability === "experimental" || groupExperimental) && !isInternal; + if (isExperimentalMethod) { + lines.push(` @experimental`); + } lines.push(sig); pushPyRpcMethodDocstring(lines, " ", method, { diff --git a/scripts/codegen/rust.ts b/scripts/codegen/rust.ts index e8543c5cf..e23da6b94 100644 --- a/scripts/codegen/rust.ts +++ b/scripts/codegen/rust.ts @@ -1904,28 +1904,58 @@ function emitNamespaceMethod( ? "pub(crate)" : "pub"; + // Gate experimental methods behind the `experimental` Cargo feature so that + // external callers must opt in explicitly to reach an experimental API — the + // Rust analog of C# `[Experimental]` / Java `@CopilotExperimental`. Rather than + // removing the method (which would also break the SDK's own internal callers), + // we make it *conditionally visible*: `pub` only when the feature is enabled, + // and `pub(crate)` otherwise. Internal SDK code can therefore always call it, + // while consumers get a hard "method not found" compile error unless they + // enable `features = ["experimental"]`. Only public methods need this; methods + // that are already `pub(crate)` are invisible to consumers regardless. + const gateExperimental = method.stability === "experimental" && fnVis === "pub"; + + const emitFnForm = (signature: string, docsIncludeParams: boolean, bodyHasParams: boolean): void => { + if (gateExperimental) { + out.push(...buildDocs(docsIncludeParams)); + out.push(` #[cfg(feature = "experimental")]`); + out.push(` pub ${signature}`); + pushNamespaceMethodBody(out, constName, isSession, bodyHasParams, resultIsVoid); + out.push(""); + // Crate-internal fallback so the SDK itself keeps compiling with the + // feature off; hidden from docs because it is not a public entry point. + out.push(` #[cfg(not(feature = "experimental"))]`); + out.push(` #[doc(hidden)]`); + out.push(` pub(crate) ${signature}`); + pushNamespaceMethodBody(out, constName, isSession, bodyHasParams, resultIsVoid); + out.push(""); + return; + } + out.push(...buildDocs(docsIncludeParams)); + out.push(` ${fnVis} ${signature}`); + pushNamespaceMethodBody(out, constName, isSession, bodyHasParams, resultIsVoid); + out.push(""); + }; + if (hasParams && paramsInfo.optional) { - out.push(...buildDocs(false)); - out.push( - ` ${fnVis} async fn ${fnName}(&self) -> Result<${returnType}, Error> {`, + emitFnForm( + `async fn ${fnName}(&self) -> Result<${returnType}, Error> {`, + false, + false, ); - pushNamespaceMethodBody(out, constName, isSession, false, resultIsVoid); - out.push(""); - out.push(...buildDocs(true)); - out.push( - ` ${fnVis} async fn ${fnName}_with_params(&self, params: ${paramsTypeName}) -> Result<${returnType}, Error> {`, + emitFnForm( + `async fn ${fnName}_with_params(&self, params: ${paramsTypeName}) -> Result<${returnType}, Error> {`, + true, + true, ); - pushNamespaceMethodBody(out, constName, isSession, true, resultIsVoid); - out.push(""); return; } - out.push(...buildDocs(hasParams)); - out.push( - ` ${fnVis} async fn ${fnName}(&self${paramArg}) -> Result<${returnType}, Error> {`, + emitFnForm( + `async fn ${fnName}(&self${paramArg}) -> Result<${returnType}, Error> {`, + hasParams, + hasParams, ); - pushNamespaceMethodBody(out, constName, isSession, hasParams, resultIsVoid); - out.push(""); } function generateRpcCode(apiSchema: ApiSchema): string { From f114b3a49e1b1f1b5333a6c9790eb30e24454491 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 18 Jun 2026 11:07:14 -0400 Subject: [PATCH 2/3] Exclude eslint-plugin from root vitest collection The standalone @github/eslint-plugin-copilot-sdk package ships its own node:test suite (run via its package's npm test). The SDK root 'vitest run' was globbing eslint-plugin/test/no-experimental-api.test.js and failing to collect it (No test suite found), breaking Node.js SDK Tests on all platforms. Exclude eslint-plugin/** from the root vitest config so the plugin's tests run only under their own runner. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- nodejs/vitest.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/nodejs/vitest.config.ts b/nodejs/vitest.config.ts index 03f6c779e..6758da974 100644 --- a/nodejs/vitest.config.ts +++ b/nodejs/vitest.config.ts @@ -15,6 +15,7 @@ export default defineConfig({ "**/dist/**", "**/*.d.ts", "**/basic-test.ts", // Old manual test + "**/eslint-plugin/**", // Standalone package with its own node:test runner ], }, }); From c79b51f18e1d2192bc794948d8bb8d19a291fdae Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 18 Jun 2026 11:43:44 -0400 Subject: [PATCH 3/3] Address review: run nested Go analyzer module in CI; fix eslint docs anchor - go/test.sh now explicitly runs the copilotexperimental nested module's tests, which the repo-root `go test ./...` does not descend into, so analyzer regressions are caught in CI. - nodejs/eslint-plugin/README.md adds a Rules section with a `no-experimental-api` heading so the rule's generated docs URL fragment (#no-experimental-api) resolves to a real anchor. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- go/test.sh | 9 +++++++++ nodejs/eslint-plugin/README.md | 10 ++++++++++ 2 files changed, 19 insertions(+) diff --git a/go/test.sh b/go/test.sh index 15fc35c30..db7727730 100755 --- a/go/test.sh +++ b/go/test.sh @@ -45,5 +45,14 @@ echo go test -v ./... -race -timeout=20m +echo +echo "=== Running copilotexperimental analyzer tests (nested module) ===" +echo + +# The analyzer lives in a nested Go module, so the repo-root `go test ./...` +# above does not descend into it. Run its tests explicitly so analyzer +# regressions are caught in CI. +(cd copilotexperimental && go test ./...) + echo echo "✅ All tests passed!" diff --git a/nodejs/eslint-plugin/README.md b/nodejs/eslint-plugin/README.md index 4768352e2..764614d04 100644 --- a/nodejs/eslint-plugin/README.md +++ b/nodejs/eslint-plugin/README.md @@ -8,6 +8,16 @@ Type-aware ESLint rules for the GitHub Copilot SDK. with `/** @experimental */`. Consumers must explicitly suppress the diagnostic to opt in at a call site. +## Rules + +### `no-experimental-api` + +Reports references to SDK symbols whose JSDoc is tagged `@experimental`. The rule is +type-aware: it resolves each referenced symbol back to its declaration and inspects the +declaration's JSDoc, so aliased imports and re-exports are flagged too. Suppress an +individual call site with an `eslint-disable-next-line` comment (see +[Suppressing a single use](#suppressing-a-single-use)). + ## Install ```bash